IT326 Project
_________________________________________________________________________________________________________
The Goal
Our primary objective of this analysis is to classify whatever a
student will go to college or not using the classification methods and
to identify the main factors and reasons why students are less likely to
pursue higher education indicated by “will_go_to_college” being ‘False’.
By leveraging the provided dataset with attributes such as school type,
school accreditation, gender, interest in college, residence, we aim to
discover the most influential variables and their relationships with the
decision not to attend college.
Sample of our data
head(Dataset)
Here are a sample of 6 row from our dataset.
Missing values
sum(is.na(Dataset))
[1] 0
There are no missing values in our dataset.
Statistical graphs
Graph 1:
df =data.frame(Dataset)
ggplot(data=df, aes(x = interest, fill = will_go_to_college)) +
geom_bar() +
scale_x_discrete(limits = c('Not Interested', 'Less Interested', 'Uncertain', 'Interested', 'Very Interested')) +
labs(title = 'College interest vs College attendance ') +
scale_fill_manual(values = c("True" = "antiquewhite2", "False" = "antiquewhite3")) +
theme_minimal()

NA
NA
According to the graph, whether students are interested in going to
college or not does not affect whether they actually end up attending
Collage . There is a group of individuals who were interested in
attending but did not receive acceptance, while others who were not
interested were accepted.
Graph 2:
filtered_True =filter(Dataset, will_go_to_college == 'True')
filtered_False =subset(Dataset, will_go_to_college =='False')
ggplot() +
geom_density(data = filtered_True, aes(x = average_grades, fill = "Going to College"), alpha = 0.5) +
geom_density(data = filtered_False, aes(x = average_grades, fill = "NOT Going to College"), alpha = 0.5) +
labs(x = "Average Grades", y = "Density") +
ggtitle("Comparison of Average Grades for Students Going to College and NOT Going to College") +
scale_fill_manual(values = c("Going to College" = "antiquewhite4", "NOT Going to College" = "antiquewhite1"))

The graph shows that the average grades for students who had accepted
to go to college were higher than those who did not enter college , and
this indicates the existence of a correlation between those who going to
college and the average grades
Graph 3:
Dataset_percentage <- Dataset %>%
group_by(type_school) %>%
summarise(percentage = mean(will_go_to_college == "True") * 100)
# Create a percentage chart
ggplot(Dataset_percentage, aes(x = type_school, y = percentage, fill = type_school)) +
geom_bar(stat = "identity") +
labs(title = "Percentage of Students Going to College by Type of School",
x = "Type of School",
y = "Percentage") +
scale_fill_manual(values = c("Academic" = "antiquewhite3", "Vocational" = "antiquewhite2")) +
theme_minimal()

NA
NA
This graph shows the impact of the type of high school attended by
students on their college attendance . Based on the bar chart:
among students from Academic high schools, 313 are going to
college, and 296 are not.
among students from Vocational high schools, 187 are going to
college, and 204 are not
These information tell us that a higher proportion of students from
academic high schools are going to college compared to those from
vocational schools which suggest that the type of school attended.
Graph 4:
ggplot(Dataset, aes(x = average_grades)) +
geom_histogram(binwidth = 5, fill = "antiquewhite2", color = "antiquewhite4") +
labs(title = "Distribution of students' grades",
x = "Students' average grades",
y = "Frequency") +
theme_minimal()

This histogram show us that the majority of the students in the
dataset are performing well since it seems like their grades are
spanning between 75 and 98. This analysis will help us determine whether
the academic performance level of students is a contributing factor to
their college attendance or not.
Statistical Measures
- The student’s academic performance analysis:
summary(Dataset$average_grades)
The student grades in our dataset range from 75.00 to 98.00, with a
median of 85.58 and an average of 86.10. This suggests that most
students are doing well as none of them have average grades below 50.
However, it’s interesting to note that some students have much higher or
lower grades than the average, mainly due to the wide range of
grades..
- The socioeconomic status of students’ families analysis:
summary(Dataset$parent_salary)
In the dataset, we’ve got students parents with salaries ranging from
1,000,000 to 10,000,000 IDR/Rupiah. The median salary is 5,440,000
IDR/Rupiah, and the average is 5,381,570 IDR/Rupiah. This data tells us
that many parents in our dataset earn less than the average salary in
Indonesia, which is 146,000,000 IDR. This suggests that quite a few
students in our dataset come from families with limited finances. And
this financial situation could certainly impact their ability to get
through college.
Additionally we can utilize the house area attribute to gain a deeper
understanding of the socioeconomic status of students’ families, where
students with houses significantly larger than the mean might indicate a
higher socioeconomic status, while those with houses considerably
smaller than the mean might reflect a comparatively lower socioeconomic
status. Based on the shown output, the house areas range from
[20.00-120.00 ㎡]. The median house area is 75.50 ㎡ indicates that
families with house areas around this value likely have moderate
socioeconomic status with houses that neither very small nor very
large.
- Understanding Parent Age Range and Variation in its Values
summary(Dataset$parent_age)
Min. 1st Qu. Median Mean 3rd Qu. Max.
40.00 50.00 52.00 52.21 54.00 65.00
SD=sd(Dataset$parent_age)
MeanAge=mean(Dataset$parent_age)
cat("coefficient of variation:",SD/MeanAge*100,"%")
coefficient of variation: 6.704771 %
This summary provides the range for age attribute [40,65] which
indicates that all parent in middle age during this age parent have more
concern about their children , the coefficient of variation= 6.7% which
indicates lower variation ,and the value of attribute parent_agerare are
relatively close to the mean overall 25% of them have an age below or
equal to 50 , 75% have an age below or equal to 54 and the median value
is 52
Outliers analysis
###parent age outliers
quartiles <- quantile(Dataset$parent_age, probs=c(.25, .75), na.rm = FALSE)
IQR <- IQR(Dataset$parent_age)
Lower <- quartiles[1] - 1.5*IQR
Upper <- quartiles[2] + 1.5*IQR
data_no_outlier <- subset(Dataset, Dataset$parent_age > Lower & Dataset$parent_age < Upper)
dim(data_no_outlier)
[1] 957 11
###parent salary outliers
quartiles <- quantile(Dataset$parent_salary, probs=c(.25, .75), na.rm = FALSE)
IQR <- IQR(Dataset$parent_salary)
Lower <- quartiles[1] - 1.5*IQR
Upper <- quartiles[2] + 1.5*IQR
data_no_outlier <- subset(data_no_outlier, data_no_outlier$parent_salary> Lower & data_no_outlier$parent_salary < Upper)
dim(data_no_outlier)
[1] 955 11
###averge grades outliers
quartiles <- quantile(Dataset$average_grades, probs=c(.25, .75), na.rm = FALSE)
IQR <- IQR(Dataset$average_grades)
Lower <- quartiles[1] - 1.5*IQR
Upper <- quartiles[2] + 1.5*IQR
data_no_outlier <- subset(data_no_outlier, data_no_outlier$average_grades> Lower & data_no_outlier$average_grades < Upper)
dim(data_no_outlier)
[1] 944 11
###house area outliers
quartiles <- quantile(Dataset$house_area, probs=c(.25, .75), na.rm = FALSE)
IQR <- IQR(Dataset$house_area)
Lower <- quartiles[1] - 1.5*IQR
Upper <- quartiles[2] + 1.5*IQR
data_no_outlier <- subset(data_no_outlier, data_no_outlier$house_area> Lower & data_no_outlier$house_area < Upper)
Founded_Outliers=data.frame(anti_join(Dataset,data_no_outlier))
Joining with `by = join_by(type_school, school_accreditation, gender, interest, residence, parent_age, parent_salary, house_area, average_grades, parent_was_in_college, will_go_to_college)`
print(Founded_Outliers)
After conducting data analysis and identifying outliers, our
inspection reveals that the detected outliers represent inherent
variation within the population. Regarding Parent_age, outliers are
observed for values below 44 and above 65. However, it should be noted
the age from 40 to 65 fall within the expected mean of our dataset
meaning that it doesn’t indicate that they are outliers . For
parent_salary, we found two outliers: one below 1,326,250 ind ≈ 85 USD
and another above 9,416,250 ind ≈ 606 USD. The minimum and maximum
values were determined to be 1,000,000 ind ≈ 64 USD and 10,000,000 ind ≈
644 USD, respectively. In the case of grades, twelve outliers were
identified, ranging from below 76 to above 97. Nevertheless, since the
data falls within the acceptable range of 0 to 100, these outliers
should be retained as they are still considered normal and within the
usual grade range. Finally, for house_area, we found eleven outliers
below 34.4m and above 115m, with the minimum being 20m and the maximum
being 120m. However, these values are still considered typical for the
population.
Normalization
normalize <- function(x) {return((x-min(x))/ (max(x)-min(x)))}
datasetWithoutNormalization<-Dataset
Dataset$parent_salary<-normalize(datasetWithoutNormalization$parent_salary)
Dataset$house_area<-normalize(datasetWithoutNormalization$house_area)
print(Dataset)
We applied normalization to the ‘parent_salary’ and ‘house_area’
attributes, scaling their values to a range between 0 and 1. This
normalization process greatly facilitates data handling and analysis,
ensuring that these attributes are on a consistent scale. Which will
improve the reliability of our data analysis and enable better
conclusions to be drawn from the dataset. Normalization is a crucial
step in preparing the data for modeling, as it prevents attributes with
larger numerical ranges from dominating the analysis and ensures fair
treatment for all features.
Discretization
Dataset$average_grades [Dataset$average_grades >= 95] <- '+A'
Dataset$average_grades [95 >Dataset$average_grades & Dataset$average_grades >= 90] <- 'A'
Dataset$average_grades [90 >Dataset$average_grades & Dataset$average_grades >= 85] <- '+B'
Dataset$average_grades [85 >Dataset$average_grades & Dataset$average_grades >= 80] <- 'B'
Dataset$average_grades [80 >Dataset$average_grades & Dataset$average_grades >= 75] <- '+C'
Dataset$average_grades [75 >Dataset$average_grades & Dataset$average_grades >= 70] <- 'C'
Dataset$average_grades [70 >Dataset$average_grades & Dataset$average_grades >= 65] <- '+D'
Dataset$average_grades [65 >Dataset$average_grades & Dataset$average_grades >= 60] <- 'D'
Dataset$average_grades [60 >Dataset$average_grades & Dataset$average_grades >= 0] <- 'F'
Dataset$average_grades <- as.character(Dataset$average_grades )
print(Dataset)
We transformed the parent_age attribute into intervals by dividing
the values to be fall on one of two possible interval labels with equal
width which is(40,50],(50,60] by discretization the values well be
simpler to classify or perform other methods that can help us later in
our model.
and to better utilize and interpret the grades attributes for each
student, we have converted the numeric grades into letter grades (A+, A,
B+, B, C+, C, D+, D, F). This transformation was undertaken to focus on
the general letter grade representation rather than the precise
numerical values.
Encoding
Dataset$parent_was_in_college[Dataset$parent_was_in_college=="TRUE"]<-1
Dataset$parent_was_in_college[Dataset$parent_was_in_college=="True"]<-1
Dataset$parent_was_in_college[Dataset$parent_was_in_college=="FALSE"]<-0
Dataset$parent_was_in_college[Dataset$parent_was_in_college=="False"]<-0
Dataset$will_go_to_college[Dataset$will_go_to_college=="TRUE"]<-0
Dataset$will_go_to_college[Dataset$will_go_to_college=="True"]<-0
Dataset$will_go_to_college[Dataset$will_go_to_college=="FALSE"]<-1
Dataset$will_go_to_college[Dataset$will_go_to_college=="False"]<-1
Dataset$gender[Dataset$gender=="Female"]<-1
Dataset$gender[Dataset$gender=="Male"]<-0
Dataset$school_accreditation[Dataset$school_accreditation=="A"]<-1
Dataset$school_accreditation[Dataset$school_accreditation=="B"]<-0
Dataset$interest[Dataset$interest=="Very Interested"]<-4
Dataset$interest[Dataset$interest=="Interested"]<-3
Dataset$interest[Dataset$interest=="Less Interested"]<-2
Dataset$interest[Dataset$interest=="Not Interested"]<-1
Dataset$interest[Dataset$interest=="Uncertain"]<-0
Dataset$type_school[Dataset$type_school=="Academic"]<-1
Dataset$type_school[Dataset$type_school=="Vocational"]<-0
Dataset$residence[Dataset$residence=="Urban"]<-1
Dataset$residence[Dataset$residence=="Rural"]<-0
print(Dataset)
Since encoding is an important step in data preprocessing that
enables the use of categorical data in various data analysis and machine
learning tasks, we encoded attributes like the ‘parent was in college’
attribute from (True, False) to (1, 0), and ‘will go to college’ from
(True, False) to (0, 1). This encoding is carried out as we aim to
predict the influencing factors. Additionally, we encoded the ‘gender’
attribute from (Female, Male) to (1, 0), ‘school accreditation’ from (A,
B) to (1, 0), ‘type_school’ from (Academic, Vocational) to (1, 0),
‘residence’ from (Urban, Rural) to (1, 0), and ‘interest’ from (Very
interested ,Interested , Less Interested , Not Interested ,Uncertain )
to (4,3,2, 1, 0) respectively. Encoding serves to simplify the data,
reduce complexity, and enhance its suitability for modeling
purposes.
Correlation analysis Chi square test for nominal attribute:
#1
C=chisq.test(Dataset$type_school , Dataset$will_go_to_college)
print(C)
Pearson's Chi-squared test with Yates' continuity correction
data: Dataset$type_school and Dataset$will_go_to_college
X-squared = 1.0751, df = 1, p-value = 0.2998
#2
C=chisq.test(Dataset$school_accreditation , Dataset$will_go_to_college)
print(C)
Pearson's Chi-squared test with Yates' continuity correction
data: Dataset$school_accreditation and Dataset$will_go_to_college
X-squared = 0.78513, df = 1, p-value = 0.3756
#3
C=chisq.test(Dataset$gender , Dataset$will_go_to_college)
print(C)
Pearson's Chi-squared test with Yates' continuity correction
data: Dataset$gender and Dataset$will_go_to_college
X-squared = 1.0249, df = 1, p-value = 0.3114
#4
C=chisq.test(Dataset$interest , Dataset$will_go_to_college)
print(C)
Pearson's Chi-squared test
data: Dataset$interest and Dataset$will_go_to_college
X-squared = 73.337, df = 4, p-value = 4.477e-15
#5
C=chisq.test(Dataset$residence , Dataset$will_go_to_college)
print(C)
Pearson's Chi-squared test with Yates' continuity correction
data: Dataset$residence and Dataset$will_go_to_college
X-squared = 0.016098, df = 1, p-value = 0.899
#6
C=chisq.test(Dataset$average_grades , Dataset$will_go_to_college)
print(C)
Pearson's Chi-squared test
data: Dataset$average_grades and Dataset$will_go_to_college
X-squared = 261.89, df = 4, p-value < 2.2e-16
#7
C=chisq.test(Dataset$parent_was_in_college , Dataset$will_go_to_college)
print(C)
Pearson's Chi-squared test with Yates' continuity correction
data: Dataset$parent_was_in_college and Dataset$will_go_to_college
X-squared = 2.1194, df = 1, p-value = 0.1454
All the attributes have X-square greater than the p-value which
indicate a some association with the class label; therefore we reject
the null hypothesis
we noticed for ‘interest’ and ‘average grade’ the analysis shows that
X-square is much larger than p-value indicate the significant
association of the two attributes with the decision of the student to go
to the collage or not
Correlation coefficient analysis for numeric attribute:
biserial.cor(Dataset$parent_salary,Dataset$will_go_to_college, c("all.obs", "complete.obs"), level = 1)
[1] 0.4756928
biserial.cor(Dataset$house_area,Dataset$will_go_to_college, c("all.obs", "complete.obs"), level = 1)
[1] 0.4672669
biserial.cor(Dataset$parent_age,Dataset$will_go_to_college,c("all.obs", "complete.obs"), level = 1)
[1] 0.04287336
the analysis shows moderate correlation coefficient for parent salary
and house area with the class label which indicate that they are
relevant factors meaning that the higher the parent salary and the
larger house area the higher probability for a student to enroll in a
collage
where is the on other hand, the correlation coefficient for the
parent age is very small which indicate that the parent age has little
impact to the probability for student to enroll in a collage
Feature selection:
ultimately based on the analysis of the correlation that we conducted
on the relationship of the dataset attributes with the class label, and
the understanding of the data and the context of each attribute and
potential relevance to the class label we decided to not delete any of
the attribute
Classification:
The attribute selection measure is a heuristic technique employed to
select the most optimal criteria for dividing a collection of data
tuples into partitions that are as pure as possible. Several attribute
selection measures are commonly utilized, such as Information Gain
(ID3), Gain Ratio (C4.5), and Gini Index (CART). we will constraints for
Each of these measures different partitions by dividing them in ratios
of 50%-50%, 70%-30%, and 80%-20%.
factor the data
data<-Preprocessed_dataset
data$will_go_to_college <- as.factor(data$will_go_to_college)
data$residence <- as.factor(data$residence)
data$gender <- as.factor(data$gender)
data$parent_was_in_college <- as.factor(data$parent_was_in_college)
data$interest <- as.factor(data$interest)
data$type_school <- as.factor(data$type_school)
data$school_accreditation <- as.factor(data$school_accreditation)
data$average_grades <- as.factor(data$average_grades)
balanced or imbalanced
library(tidyverse)
library(caret)
data$will_go_to_college<- as.numeric(data$will_go_to_college)
hist(data$will_go_to_college,col="coral")

prop.table(table(data$will_go_to_college))
1 2
0.5 0.5
we want to confirm that the distribution between the two label data
is not too much different. Because imbalanced datasets can lead to
imbalanced accuracy.
Fortunately ,our data is balanced
set.seed(123)
ind=sample(2, nrow(data), replace=TRUE, prob=c(0.70 , 0.30))
train_data=data[ind==1,]
test_data=data[ind==2,]
dim(train_data)
[1] 705 11
dim(test_data)
[1] 295 11
We partition the data the data into (70% training, 30% testing). This
result in 705 object in the training set and 295 object in the testing
set.
information gain
library(party)
myFormula<- will_go_to_college~ + type_school+school_accreditation+gender+interest+residence+parent_age+parent_salary+house_area+average_grades+parent_was_in_college
dataset_ctree<-ctree(myFormula, data=train_data)
table(predict(dataset_ctree), train_data$will_go_to_college)
0 1
0 309 48
1 37 311
plot(dataset_ctree,type="simple")

testPred <- predict(dataset_ctree, newdata = test_data)
result<-table(testPred, test_data$will_go_to_college )
library(caret)
co_result <- confusionMatrix(result,positive="1")
print(co_result)
Confusion Matrix and Statistics
testPred 0 1
0 136 34
1 18 107
Accuracy : 0.8237
95% CI : (0.7754, 0.8655)
No Information Rate : 0.522
P-Value [Acc > NIR] : < 2e-16
Kappa : 0.6451
Mcnemar's Test P-Value : 0.03751
Sensitivity : 0.7589
Specificity : 0.8831
Pos Pred Value : 0.8560
Neg Pred Value : 0.8000
Prevalence : 0.4780
Detection Rate : 0.3627
Detection Prevalence : 0.4237
Balanced Accuracy : 0.8210
'Positive' Class : 1

this matrix below show the evaluation model on testing data
| Accuracy |
82.37% |
| Error rate |
17.63% |
| Sensitivity |
75.89% |
| Specificity |
88.31% |
| Precision |
85.60% |
In this decision tree model, the average grade with the highest
information gain is selected as the root attribute. This means that the
average grade is considered the most informative feature for classifying
the data.
To enhance the model’s predictive capability, additional splitting
criteria are incorporated based on their information gain values. These
criteria include type_school, interest, residence, parent_salary, and
house_area. By considering these attributes, the model aims to capture
more relevant information from the data and improve its classification
accuracy.
The model then utilizes the house area and average grades as
splitting criteria to separate the data into different branches of the
tree in multiples levels
However, it’s worth noting that the decision tree in this case is
considered to have a low level. This implies that the tree is relatively
shallow and may not be able to capture intricate patterns or
relationships present in the data. For example gender,
parent_was_in_college, parent age, and school accreditation are not
included in the tree due to limited availability of the data or their
lower information gain values.
Gain ratio
library(RWeka)
C45Fit <- J48( will_go_to_college~.,data=train_data)
table(train_data$will_go_to_college, predict(C45Fit))
0 1
0 322 24
1 27 332
plot(C45Fit,type = "simple")

testPred <- predict(C45Fit, newdata = test_data)
results <- confusionMatrix(testPred, test_data$will_go_to_college, positive = "1")
print(results)
Confusion Matrix and Statistics
Reference
Prediction 0 1
0 128 32
1 26 109
Accuracy : 0.8034
95% CI : (0.7534, 0.8472)
No Information Rate : 0.522
P-Value [Acc > NIR] : <2e-16
Kappa : 0.6053
Mcnemar's Test P-Value : 0.5115
Sensitivity : 0.7730
Specificity : 0.8312
Pos Pred Value : 0.8074
Neg Pred Value : 0.8000
Prevalence : 0.4780
Detection Rate : 0.3695
Detection Prevalence : 0.4576
Balanced Accuracy : 0.8021
'Positive' Class : 1

this matrix below show the evaluation model on testing data
| Accuracy |
80.34% |
| Error rate |
19.66% |
| Sensitivity |
77.30% |
| Specificity |
83.12% |
| Precision |
80.74% |
In contrast to the previous decision tree, the current tree is more
complex and includes all the attributes except for gender. This means
that the decision tree takes into account a wider range of factors to
make its classifications. The attribute with the highest Gain ratio,
which measures the effectiveness of a split, is chosen as the root
attribute. In this case, Parent salary is determined to have the highest
Gain ratio and is selected as the root attribute.
The model then repeatedly uses average grades as the splitting
criterion at different levels of the tree. This indicates that the
average grades are considered crucial for classifying the data and
further subdividing the branches.
Gini index
library(rpart)
library(rpart.plot)
cart_fit=rpart(will_go_to_college~., data=train_data, method="class",cp=0.008)
rpart.plot(cart_fit)

testPred<- predict(cart_fit,newdata=test_data,type="class")
results<- confusionMatrix(testPred,test_data$will_go_to_college,positive="1")
print(results)
Confusion Matrix and Statistics
Reference
Prediction 0 1
0 125 31
1 29 110
Accuracy : 0.7966
95% CI : (0.7461, 0.8411)
No Information Rate : 0.522
P-Value [Acc > NIR] : <2e-16
Kappa : 0.5922
Mcnemar's Test P-Value : 0.8973
Sensitivity : 0.7801
Specificity : 0.8117
Pos Pred Value : 0.7914
Neg Pred Value : 0.8013
Prevalence : 0.4780
Detection Rate : 0.3729
Detection Prevalence : 0.4712
Balanced Accuracy : 0.7959
'Positive' Class : 1
this matrix below show the evaluation model on testing data
| Accuracy |
79.66% |
| Error rate |
20.34% |
| Sensitivity |
78.01% |
| Specificity |
81.17% |
| Precision |
79.14% |
In this particular partition of the decision tree, Parent salary was
once again chosen as the root attribute due to its high Gain index.
Gender and residence, on the other hand, were not included in this
partition of the tree. Without sufficient data or a significant
information gain associated with these attributes, the model may not
have enough evidence to utilize them for accurate classification.
It’s worth noting that gender was not included in this partition, and
it is important to highlight that the accuracy of this specific model
was relatively lower, at 79.66%.
set.seed(123)
ind=sample(2, nrow(data), replace=TRUE, prob=c(0.80 , 0.20))
train_data=data[ind==1,]
test_data=data[ind==2,]
dim(train_data)
[1] 802 11
dim(test_data)
[1] 198 11
We partition the data the data into (80% training, 20% testing). This
result in 802 object in the training set and 198 object in the testing
set.
Gain ratio
library(RWeka)
C45Fit <- J48( will_go_to_college~.,data=train_data)
table(train_data$will_go_to_college, predict(C45Fit))
0 1
0 378 22
1 16 386
plot(C45Fit,type = "simple")

testPred <- predict(C45Fit, newdata = test_data)
results <- confusionMatrix(testPred, test_data$will_go_to_college, positive = "1")
print(results)
Confusion Matrix and Statistics
Reference
Prediction 0 1
0 76 15
1 24 83
Accuracy : 0.803
95% CI : (0.7407, 0.856)
No Information Rate : 0.5051
P-Value [Acc > NIR] : <2e-16
Kappa : 0.6064
Mcnemar's Test P-Value : 0.2002
Sensitivity : 0.8469
Specificity : 0.7600
Pos Pred Value : 0.7757
Neg Pred Value : 0.8352
Prevalence : 0.4949
Detection Rate : 0.4192
Detection Prevalence : 0.5404
Balanced Accuracy : 0.8035
'Positive' Class : 1

this matrix below show the evaluation model on testing data
| Accuracy |
80.03% |
| Error rate |
19.97% |
| Sensitivity |
84.69% |
| Specificity |
76.00% |
| Precision |
77.57% |
In this particular model that utilizes the gain ratio, it is observed
that the decision tree is once again more complex compared to the other
models. The attribute chosen as the root attribute is parent salary,
based on its high purity and resulting high gain ratio. However, it is
important to note that the gender attribute was not included in the
decision tree. Ultimately, the gain ratio model’s construction involved
careful considerations and prioritized the parent’s salary as root
Gini index
library(rpart)
library(rpart.plot)
cart_fit=rpart(will_go_to_college~., data=train_data, method="class",cp=0.008)
rpart.plot(cart_fit)

testPred<- predict(cart_fit,newdata=test_data,type="class")
results<- confusionMatrix(testPred,test_data$will_go_to_college,positive="1")
print(results)
Confusion Matrix and Statistics
Reference
Prediction 0 1
0 85 23
1 15 75
Accuracy : 0.8081
95% CI : (0.7462, 0.8605)
No Information Rate : 0.5051
P-Value [Acc > NIR] : <2e-16
Kappa : 0.6158
Mcnemar's Test P-Value : 0.2561
Sensitivity : 0.7653
Specificity : 0.8500
Pos Pred Value : 0.8333
Neg Pred Value : 0.7870
Prevalence : 0.4949
Detection Rate : 0.3788
Detection Prevalence : 0.4545
Balanced Accuracy : 0.8077
'Positive' Class : 1
this matrix below show the evaluation model on testing data
| Accuracy |
80.81% |
| Error rate |
19.19% |
| Sensitivity |
76.53% |
| Specificity |
85.00% |
| Precision |
83.33% |
for the first time house area was chosen as the root due to its high
purity, as indicated by the high Gini index.
The model relies on the parent_salary attribute to split the tree
multiple times, indicating its importance in determining the
classification. This suggests that the parent’s salary has a significant
impact on whether a student will go to college or not, and it is
repeatedly used as a criterion for further branching in the tree.
It’s worth noting that 20% of the dataset follows the rule:
IF house_area < 0.54 and parent_salary < 0.42, THEN will go to
college = 1.
This rule implies that when the house area is below a certain
threshold and the parent’s salary is below another threshold, there is a
higher likelihood of the student going to college.
Moreover, 28% of the dataset follows the rule:
IF house_area >= 0.54 and parent_salary >= 0.37 and type_school
= 1, THEN will go to college = 1. This rule suggests that when the house
area is above a certain threshold, the parent’s salary is above another
threshold, and the type of school is 1, there is a higher probability of
the student going to college.
Overall, these rules cover a significant portion of the dataset, 48%
of it. This indicates that a large amount of data can be classified
based on these specific rules, providing valuable insights into the
factors that influence a student’s likelihood of attending college. we
only mentioned those two rules duo to their high percentage of data
set.seed(123)
ind=sample(2, nrow(data), replace=TRUE, prob=c(0.50 , 0.50))
train_data=data[ind==1,]
test_data=data[ind==2,]
dim(train_data)
[1] 493 11
dim(test_data)
[1] 507 11
We partition the data the data into (50% training, 50% testing). This
result in 493 object in the training set and 507 object in the testing
set.
information gain
library(party)
myFormula<- will_go_to_college~ + type_school+school_accreditation+gender+interest+residence+parent_age+parent_salary+house_area+average_grades+parent_was_in_college
dataset_ctree<-ctree(myFormula, data=train_data)
table(predict(dataset_ctree), train_data$will_go_to_college)
0 1
0 211 23
1 40 219
plot(dataset_ctree,type="simple")

testPred <- predict(dataset_ctree, newdata = test_data)
result<-table(testPred, test_data$will_go_to_college )
library(caret)
co_result <- confusionMatrix(result, positive = "1")
print(co_result)
Confusion Matrix and Statistics
testPred 0 1
0 196 27
1 53 231
Accuracy : 0.8422
95% CI : (0.8075, 0.8729)
No Information Rate : 0.5089
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.6837
Mcnemar's Test P-Value : 0.005189
Sensitivity : 0.8953
Specificity : 0.7871
Pos Pred Value : 0.8134
Neg Pred Value : 0.8789
Prevalence : 0.5089
Detection Rate : 0.4556
Detection Prevalence : 0.5602
Balanced Accuracy : 0.8412
'Positive' Class : 1

this matrix below show the evaluation model on testing data
| Accuracy |
84.22% |
| Error rate |
15.78% |
| Sensitivity |
89.53% |
| Specificity |
78.71% |
| Precision |
81.34% |
In this decision tree model, the root attribute is determined by
selecting the average grade with the highest information gain. This
indicates that the average grade is considered the most informative
feature for classifying the data, as it provides significant insights
into the classification process.
To improve the model’s predictive capability, additional splitting
criteria are incorporated based on their information gain values. These
criteria include type_school, interest, residence, parent_salary,
house_area, and whether the parent was in college. By considering these
attributes, the model aims to capture more relevant information and
enhance its ability to classify the data accurately.
In the tree structure, the model utilizes both the house area and
average grades as splitting criteria in multiple levels. This means that
the data is divided into different branches of the tree based on these
attributes, allowing for more refined classification.
gender, parent age, and school accreditation are not included in the
tree due to either limited availability of the data or their lower
information gain values, indicating that they may have less impact on
the classification process according to the model’s evaluation.
Gain ratio
library(RWeka)
C45Fit <- J48( will_go_to_college~.,data=train_data)
table(train_data$will_go_to_college, predict(C45Fit))
0 1
0 234 17
1 13 229
plot(C45Fit,type = "simple")

testPred <- predict(C45Fit, newdata = test_data)
results <- confusionMatrix(testPred, test_data$will_go_to_college, positive = "1")
print(results)
Confusion Matrix and Statistics
Reference
Prediction 0 1
0 206 32
1 43 226
Accuracy : 0.8521
95% CI : (0.8181, 0.8818)
No Information Rate : 0.5089
P-Value [Acc > NIR] : <2e-16
Kappa : 0.7038
Mcnemar's Test P-Value : 0.2482
Sensitivity : 0.8760
Specificity : 0.8273
Pos Pred Value : 0.8401
Neg Pred Value : 0.8655
Prevalence : 0.5089
Detection Rate : 0.4458
Detection Prevalence : 0.5306
Balanced Accuracy : 0.8516
'Positive' Class : 1

this matrix below show the evaluation model on testing data
| Accuracy |
85.21% |
| Error rate |
14.79% |
| Sensitivity |
87.60% |
| Specificity |
82.73% |
| Precision |
84.01% |
In contrast to the previous decision tree, the current tree is more
complex and includes all the attributes except for parent age. Notably,
for the first time, gender is included as an attribute in the tree. This
indicates that the decision tree now takes into account a wider range of
factors, including gender, to make its classifications. By incorporating
these additional attributes, the model aims to capture more nuanced
patterns and relationships in the data.
The attribute with the highest Gain ratio is chosen as the root
attribute. In this particular case, Parent salary is determined to have
the highest Gain ratio and is selected as the root attribute. This
suggests that Parent salary provides the most significant reduction in
uncertainty and is considered crucial for classifying the data
accurately.
Gini index
library(rpart)
library(rpart.plot)
cart_fit=rpart(will_go_to_college~., data=train_data, method="class",cp=0.008)
rpart.plot(cart_fit)

testPred<- predict(cart_fit,newdata=test_data,type="class")
results<- confusionMatrix(testPred,test_data$will_go_to_college,positive="1")
print(results)
Confusion Matrix and Statistics
Reference
Prediction 0 1
0 204 30
1 45 228
Accuracy : 0.8521
95% CI : (0.8181, 0.8818)
No Information Rate : 0.5089
P-Value [Acc > NIR] : <2e-16
Kappa : 0.7037
Mcnemar's Test P-Value : 0.106
Sensitivity : 0.8837
Specificity : 0.8193
Pos Pred Value : 0.8352
Neg Pred Value : 0.8718
Prevalence : 0.5089
Detection Rate : 0.4497
Detection Prevalence : 0.5385
Balanced Accuracy : 0.8515
'Positive' Class : 1
this matrix below show the evaluation model on testing data
The attribute chosen as the root attribute in this decision
tree is average grades, primarily due to its high purity, indicating a
high Gini index. The Gini index measures the impurity of a node in a
decision tree, and a high purity value suggests that the attribute is
effective for classification.
| Accuracy |
85.21% |
| Error rate |
24.79% |
| Sensitivity |
88.37% |
| Specificity |
81.93% |
| Precision |
83.52% |
27% of the dataset follows the rule:
IF average_grades=+A,+B,A and parent_salary>=0.37 and
house_area>=0.41 and type_school=1, THEN will_go_to_collage=0.
Furthermore, 17% of the dataset follows the rule:
IF average_grades=B,+C and house_area<=0.41, THEN
will_go_to_collage=1.
These rules highlight the complexity and variability in determining
whether a student will go to college. Even if a student has stable
financial circumstances and good grades, it does not guarantee their
enrollment in college. in contract to the second which mean Other
factors could play a role in the decision-making process.
we only mentioned those two rules duo to their high percentage of
data
final analysis
gain ratio
Gain Ratio is an normaliztion for Information Gain and is used in the
C4.5 algorithm, which is an extension of ID3. While Information Gain
tends to favor attributes with many multivariate It is calculated by
dividing the Information Gain by SplitInfo a(D).
for our trees,
Parent salary was chosen as the root attribute for all 3 trees due to
its high Gain ratio, the trees split are unbalanced since gain ratio
Tends to prefer unbalanced splits in which one partition is much smaller
than the others
| Accuracy |
85.21% |
80.34% |
80.03% |
| Error rate |
14.79% |
19.66% |
19.97% |
| Sensitivity |
87.60% |
77.30% |
84.69% |
| Specificity |
82.73% |
83.12% |
76.00% |
| Precision |
84.01% |
80.74% |
77.57% |
the evaluation method table shows that the best partition for gain
ratio shows that 50%-50% is the best model based on accuracy(tuples that
are correctly classified) with percntages 85.21% and Sensitivity (True
positive recognition rate) with 87.60% and Precision(tuples labeled as
positive are actually positive) with percentages 84.01% but the lowest
Specificity(True negative recognition rate) with 82.73%. overall it’s
the best model dor gain ratio with the lowest error rate 14.79%
gini index
Gini Index is a criterion used in the CART (Classification and
Regression Trees) algorithm for attribute selection. It measures the
impurity of a set of tuples, with lower values Gini a(D) indicating
higher purity. The Gini Index is calculated by summing the squared
probabilities of each class label in the set
for our trees,
gini index had different root (parent salary/house area/averages
grades) for each tree
| Accuracy |
85.21% |
79.66% |
80.81% |
| Error rate |
14.79% |
20.34% |
19.19% |
| Sensitivity |
88.37% |
78.01% |
76.53% |
| Specificity |
81.93% |
81.17% |
85.00% |
| Precision |
83.52% |
79.14% |
83.33% |
the evaluation method table shows that the best partition for gain
ratio shows that 50%-50% is the best model based on accuracy(tuples that
are correctly classified) with percentages 85.21% and Sensitivity (True
positive recognition rate) with 88.37% and Precision(tuples labeled as
positive are actually positive) with percentages 83.52% overall it’s the
best model for gini index with the lowest error rate 14.79%
noticeable notes
important to note that gender was not included in 7 of 9 trees
which
parent’s salary was the root of 4 of 9 trees
average grades was the root of 4 of 9 trees
house area was the root only once 1 of 9 tree
In summary, the analysis of the decision trees reveals that financial
circumstances, as indicated by parent’s salary, and academic
performance, as indicated by average grades, are crucial factors for
contemporary universities when predicting college enrollment. In this
particular dataset, gender is not considered highly important in the
decision-making process.
best model
the 50% training, 50% testing was the best partition for all
attribute selection measures with accuracies 85.21%. for both gini index
and gain ratio
Both attribute selection measures yield the same accuracy and error
rate, so we need to check the remaining evaluation metrics that is
provided:
| Accuracy |
85.21% |
85.21% |
| Error rate |
14.79% |
14.79% |
| Sensitivity |
87.60% |
88.37% |
| Specificity |
82.73% |
81.93% |
| Precision |
84.01% |
83.52% |
Comparing the sensitivity, specificity, and precision:
- Sensitivity: Gini Index has a higher sensitivity value (88.37%)
compared to Gain Ratio (87.60%). Higher sensitivity indicates a better
ability to correctly identify positive instances so gini index
is better regarding Sensitivity.
- Specificity: Gini Index has a lower specificity value (81.93%)
compared to Gain Ratio (82.73%). Higher specificity indicates a better
ability to correctly identify negative instances.
- Precision: Gain Ratio has a higher precision value (84.01%)
compared to Gini Index (83.52%). Higher precision indicates a better
ability to correctly classify positive instances.
so gain ratio is better regarding
Specificity and Precision .
Based on these comparisons, it is difficult to determine definitively
which attribute selection measure is the best. The choice between Gini
Index and Gain Ratio may depend on the specific requirements and
priorities of the problem but since we focus on the true positive more
than true negative because our goal of classification to determine who
will go to collage, so we will choose the model based on the sensitivity
(Gini index)
our final tree is:

Clustering Analysis:
In this analysis, we apply K-means clustering to the dataset using
different values of K. K-means clustering is an unsupervised learning
algorithm that partitions the data into K clusters based on similarity.
We will explore three different values of K and evaluate the clustering
results using various metrics.
Removing the class label and preparing the dataset for
Clustering
original_data <- Preprocessed_dataset
# Remove any non-numeric attributes
numeric_data <- original_data[, sapply(original_data, is.numeric)]
# Remove the class label 'will_go_to_college'
numeric_data <- numeric_data[, !(names(numeric_data) == 'will_go_to_college')]
Now, the ‘numeric_dataset’ dataset contains only numeric attributes
without the class label, which makes it ready for the clustering
process.
___________________________________________________________________________________
K=2
In our exploration of k-means clustering, we will start by examining
the dataset with K=2, representing an initial attempt to partition the
data into two distinct clusters. This initial analysis will serve as a
foundational step in understanding the inherent structure and
patterns
# k-means clustering set a seed for random number generation to make the results reproducible
set.seed(8953)
# run kmeans clustering to find 2 clusters
kmeans.result <- kmeans(numeric_data, 2)
# visualize clustering
library(factoextra)
fviz_cluster(kmeans.result, data = numeric_data)

# print the clustering result
print(kmeans.result)
K-means clustering with 2 clusters of sizes 531, 469
Cluster means:
type_school school_accreditation gender interest residence parent_age parent_salary house_area
1 0.5856874 0.4067797 0.5235405 2.001883 0.3163842 49.67985 0.5219000 0.5351620
2 0.6353945 0.5650320 0.4413646 2.296375 0.7910448 55.07036 0.4471476 0.5564648
parent_was_in_college
1 0.700565
2 0.315565
Clustering vector:
[1] 2 2 1 1 2 1 1 2 1 1 2 1 1 2 1 2 1 1 1 1 1 2 1 2 2 1 2 2 1 1 1 2 2 1 1 2 2 1 2 2 1 2 2 1 1 1 1 2
[49] 2 2 1 2 1 1 1 2 2 1 1 2 1 1 1 1 2 2 1 1 1 2 2 1 1 2 2 2 2 2 1 1 2 1 2 1 2 2 2 1 1 2 2 2 2 1 1 1
[97] 1 2 1 1 1 2 1 1 1 2 1 2 2 2 1 1 1 1 1 1 1 2 2 2 1 1 1 1 2 2 1 1 1 2 2 1 2 1 1 1 2 1 2 2 2 2 1 1
[145] 2 1 1 2 1 1 1 1 2 1 2 1 2 1 2 2 2 1 2 2 1 2 2 2 1 1 2 1 2 1 2 1 1 1 1 1 2 1 2 1 1 2 2 2 2 2 2 1
[193] 1 2 2 2 1 2 2 2 2 2 2 2 1 2 2 1 2 1 1 1 1 2 2 1 2 1 1 1 2 1 2 1 2 1 2 1 2 1 2 2 2 1 1 2 2 1 1 2
[241] 2 1 1 1 2 2 1 1 1 2 2 1 1 2 1 1 1 2 1 2 2 2 1 1 2 1 2 1 2 1 1 1 1 2 1 1 1 2 1 2 1 1 2 1 1 2 2 2
[289] 1 1 2 1 2 1 1 2 1 1 1 2 1 2 1 1 1 1 1 2 1 2 1 2 2 2 2 2 1 2 2 1 2 2 2 1 1 2 1 2 1 2 2 1 1 2 2 2
[337] 1 1 2 1 1 1 1 1 1 1 1 2 1 2 1 1 1 1 2 1 1 2 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 1 1 2 1 2 2 1 2 1 2
[385] 2 1 2 2 1 2 1 2 1 1 1 2 1 1 1 1 2 2 1 2 1 2 2 1 2 1 1 2 1 2 1 2 2 1 2 2 1 1 2 2 2 2 1 2 2 2 1 1
[433] 1 2 2 1 2 1 2 1 2 1 2 2 2 1 1 1 1 2 1 1 1 2 1 2 1 1 2 1 1 2 2 1 1 1 1 1 2 1 2 1 2 2 1 1 1 1 1 1
[481] 2 1 1 1 2 1 2 2 1 1 1 1 1 2 1 2 1 2 1 2 2 2 2 1 2 2 2 1 2 2 1 1 1 2 2 2 2 1 1 2 1 2 2 1 1 1 2 1
[529] 1 1 1 2 1 1 2 2 1 1 1 1 1 1 1 2 1 1 1 1 2 2 1 2 2 2 2 1 1 1 1 1 1 2 2 1 2 1 2 2 1 2 2 2 2 1 1 2
[577] 2 1 1 2 1 1 2 1 2 2 1 1 1 2 1 2 2 2 1 1 2 2 1 2 2 2 1 2 2 1 2 1 2 1 1 1 1 1 2 2 1 1 2 2 1 1 2 1
[625] 2 2 1 1 2 2 1 1 2 1 2 2 1 1 1 2 1 2 1 2 2 2 2 1 1 2 2 2 2 1 1 1 2 2 2 1 2 2 1 2 1 1 1 1 2 1 1 1
[673] 1 2 2 1 2 2 2 2 1 2 1 2 2 2 1 1 1 2 1 1 1 1 2 1 1 2 1 1 2 1 2 1 1 1 1 2 1 2 1 1 2 1 1 2 2 2 1 1
[721] 1 2 2 1 2 2 1 1 2 1 2 1 2 1 1 1 1 2 1 2 1 1 1 1 2 1 2 2 1 2 1 1 2 2 1 1 1 1 2 1 2 2 1 2 2 2 2 2
[769] 1 2 2 2 1 1 1 1 2 1 2 1 1 2 2 2 2 2 2 1 1 2 1 2 2 2 2 1 1 2 2 1 1 1 2 1 2 2 1 2 2 1 1 2 1 1 1 1
[817] 1 1 1 2 2 2 2 1 2 1 1 2 1 2 1 1 2 2 1 1 1 2 1 1 2 1 2 2 2 1 1 2 2 1 1 2 1 2 2 2 1 2 1 1 2 2 2 2
[865] 1 2 2 1 1 2 2 1 1 2 2 1 2 2 1 2 1 2 2 1 2 2 1 2 2 1 1 2 1 2 1 2 2 1 1 1 1 1 2 2 2 1 1 1 2 1 2 2
[913] 1 2 2 2 1 1 2 1 1 2 2 1 1 1 2 2 2 2 1 1 2 2 1 1 2 2 1 2 2 2 2 1 1 1 2 2 2 2 2 1 1 1 1 1 2 2 1 2
[961] 1 1 1 2 2 1 1 1 1 2 2 2 1 2 2 1 2 1 2 1 1 1 1 1 2 2 2 1 2 1 2 1 2 2 1 1 1 1 2 1
Within cluster sum of squares by cluster:
[1] 5022.712 3646.347
(between_SS / total_SS = 45.9 %)
Available components:
[1] "cluster" "centers" "totss" "withinss" "tot.withinss" "betweenss"
[7] "size" "iter" "ifault"
The Silhouette coefficient
#average for each cluster
avg_sil <- silhouette(kmeans.result$cluster, dist(numeric_data))
#k-means clustering with estimating k and initializations
fviz_silhouette(avg_sil)
NA

The total within-cluster sum of squares
# Calculate total within-cluster sum of squares
total_withinss <- kmeans.result$tot.withinss
cat("Total Within-Cluster Sum of Squares:", sum(total_withinss), "\n")
Total Within-Cluster Sum of Squares: 8669.059
true_labels <- c(1, 1, 2, 1, 2, 2, 3, 3, 4, 4)
cluster_assignments <- kmeans.result$cluster
BCubed recall and precision
# Calculate BCubed precision
precision <- 0
for (i in unique(true_labels)) {
cluster_indices <- which(true_labels == i)
precision <- precision + sum((table(cluster_assignments[cluster_indices]) * (table(cluster_assignments[cluster_indices]) - 1)) / sum(table(cluster_assignments[cluster_indices])))
}
precision <- precision / sum(table(cluster_assignments))
# Calculate BCubed recall
recall <- 0
for (j in unique(cluster_assignments)) {
cluster_indices <- which(cluster_assignments == j)
recall <- recall + sum((table(true_labels[cluster_indices]) * (table(true_labels[cluster_indices]) - 1)) / sum(table(true_labels[cluster_indices])))
}
recall <- recall / sum(table(true_labels))
cat("BCubed Precision:", precision, "\n")
BCubed Precision: 0.002333333
cat("BCubed Recall:", recall, "\n")
BCubed Recall: 0.1166667
Cluster Analysis for K=2:
So, the clusters are moderately compact with a reasonable silhouette
width, but low precision and recall suggest potential misclassification
issues.
___________________________________________________________________________________
K=5
Moving to K=5, our exploration in k-means clustering advances to a
configuration with five clusters. This adjustment allows for a more
detailed partitioning of the dataset, potentially revealing finer
grained patterns and distinctions among the data points. The analysis at
K=5 will offer insights into the nature of these clusters, assessing
their characteristics, compactness, and separation, as we strive to
optimize the clustering configuration for the specific nuances of our
dataset.
# k-means clustering set a seed for random number generation to make the results reproducible
set.seed(8953)
# run kmeans clustering to find 4 clusters
kmeans.result <- kmeans(numeric_data, 5)
# visualize clustering
library(factoextra)
fviz_cluster(kmeans.result, data = numeric_data)

# print the clustering result
print(kmeans.result)
K-means clustering with 5 clusters of sizes 236, 66, 173, 282, 243
Cluster means:
type_school school_accreditation gender interest residence parent_age parent_salary house_area
1 0.6440678 0.5466102 0.4067797 2.072034 0.7966102 55.20339 0.4442655 0.5555551
2 0.6818182 0.6515152 0.5151515 2.181818 1.0000000 59.21212 0.3625589 0.5229545
3 0.5086705 0.3815029 0.5722543 2.236994 0.1560694 46.96532 0.5402954 0.4870520
4 0.6950355 0.5248227 0.4893617 3.517730 0.6950355 52.21631 0.4928172 0.5503901
5 0.5267490 0.3909465 0.4855967 0.526749 0.2551440 51.11934 0.5169547 0.5763663
parent_was_in_college
1 0.3008475
2 0.1060606
3 0.8265896
4 0.4042553
5 0.7613169
Clustering vector:
[1] 1 1 4 3 1 3 4 4 5 3 1 3 3 4 4 4 3 3 3 3 4 1 4 2 4 5 1 5 5 5 3 1 2 5 4 4 1 3 1 5 5 4 4 4 4 5 5 1
[49] 1 5 3 1 5 5 5 1 1 3 3 1 5 3 5 3 1 1 5 4 5 4 4 5 3 1 1 1 1 4 5 4 4 5 2 5 1 1 1 5 4 4 1 1 2 4 3 3
[97] 3 4 4 5 5 1 5 5 4 5 3 4 2 1 5 4 5 4 3 3 5 4 2 1 3 3 4 3 1 1 3 5 5 4 1 3 5 4 5 5 4 3 4 4 4 2 3 3
[145] 5 4 5 1 3 3 3 3 2 5 1 3 1 5 1 1 1 4 1 1 5 5 1 2 3 5 1 5 2 5 4 3 4 5 5 5 4 4 1 5 3 1 1 4 4 4 1 3
[193] 5 1 1 1 3 5 1 4 1 2 4 1 4 1 2 3 4 4 3 5 3 4 1 4 1 4 5 4 2 4 1 5 5 5 5 3 4 5 1 5 5 5 5 2 1 5 4 1
[241] 1 4 5 5 4 4 3 5 5 1 2 4 5 1 5 3 4 1 3 4 2 4 3 4 4 5 1 5 4 5 3 5 3 1 5 3 5 4 3 1 5 5 1 5 4 4 1 1
[289] 4 4 2 4 2 4 3 1 5 4 3 5 4 1 3 5 5 5 3 5 3 1 3 4 5 1 1 4 4 1 1 3 2 2 1 3 5 1 3 4 3 4 4 4 4 2 4 1
[337] 3 5 5 5 5 4 5 4 5 3 5 1 5 4 4 5 5 3 1 3 4 1 5 3 5 3 5 5 5 3 4 5 3 1 1 1 2 1 4 5 1 5 4 4 4 4 4 1
[385] 4 4 1 2 3 1 5 2 3 5 3 2 4 5 4 3 4 1 5 1 3 4 4 5 1 3 4 1 4 2 4 1 5 4 5 4 4 3 1 1 2 1 3 1 1 1 3 5
[433] 3 1 1 3 4 3 1 4 5 5 2 1 1 3 5 3 4 4 4 4 5 4 3 4 4 4 4 4 5 2 2 3 4 5 4 4 4 5 4 3 1 1 5 4 5 3 4 3
[481] 1 3 3 5 2 4 4 1 4 5 3 5 4 4 4 4 3 2 3 4 4 4 1 5 1 1 1 3 1 4 3 3 5 2 1 1 1 3 3 1 5 2 1 4 5 4 1 5
[529] 3 3 3 1 4 5 2 4 4 5 3 4 3 5 4 4 3 4 5 5 1 1 5 2 4 1 5 3 4 3 5 4 3 4 1 3 5 4 4 2 4 4 1 1 1 5 4 1
[577] 1 5 5 4 5 5 1 3 5 1 4 5 5 1 3 1 1 4 5 3 1 4 4 1 1 1 4 4 1 4 1 5 4 5 4 5 5 3 2 1 4 5 2 5 4 4 1 3
[625] 1 2 4 3 2 4 5 5 2 5 1 1 5 4 4 2 3 2 5 1 5 1 4 5 4 1 1 4 2 5 4 3 4 5 1 5 2 4 3 2 4 4 3 3 1 5 4 5
[673] 5 1 1 3 1 2 1 1 5 1 3 4 1 2 4 4 4 1 5 3 3 4 5 4 5 5 5 3 4 4 5 3 5 4 4 1 5 4 3 4 1 3 4 4 1 2 5 4
[721] 4 2 4 4 1 1 3 3 4 3 1 4 4 3 3 3 4 4 4 5 3 5 3 5 1 5 1 4 3 5 5 5 5 1 4 5 3 3 1 4 1 1 4 1 1 5 2 2
[769] 5 4 4 1 5 5 3 3 5 3 2 5 5 4 5 5 1 1 4 5 4 5 5 1 1 1 5 4 3 1 4 4 3 5 4 5 4 4 5 4 1 5 3 1 3 5 3 5
[817] 3 5 5 4 4 1 1 5 2 4 5 1 4 2 3 5 1 1 3 5 5 1 5 3 1 5 1 1 1 4 4 1 1 5 4 1 3 4 2 1 3 4 4 3 4 4 4 2
[865] 5 4 2 5 3 1 1 4 3 4 4 4 1 1 4 4 5 2 1 3 2 1 4 1 1 3 3 1 5 4 3 1 4 3 5 5 4 5 4 2 4 3 4 5 2 5 4 1
[913] 4 5 2 5 5 5 4 5 4 1 1 4 5 5 1 4 1 1 3 4 1 1 3 4 4 1 4 1 1 1 1 5 5 4 1 1 4 1 2 4 4 4 5 4 1 1 5 1
[961] 5 4 3 1 1 4 5 5 5 5 1 1 5 4 4 3 1 4 1 4 5 4 3 3 1 4 2 3 1 3 1 4 4 4 4 3 5 3 5 4
Within cluster sum of squares by cluster:
[1] 967.3544 291.1379 1316.6286 925.8275 802.8925
(between_SS / total_SS = 73.2 %)
Available components:
[1] "cluster" "centers" "totss" "withinss" "tot.withinss" "betweenss"
[7] "size" "iter" "ifault"
The Silhouette coefficient
#average for each cluster
avg_sil <- silhouette(kmeans.result$cluster, dist(numeric_data))
#k-means clustering with estimating k and initializations
fviz_silhouette(avg_sil)
NA

The total within-cluster sum of squares
# Calculate total within-cluster sum of squares
total_withinss <- kmeans.result$tot.withinss
cat("Total Within-Cluster Sum of Squares:", sum(total_withinss), "\n")
Total Within-Cluster Sum of Squares: 4303.841
true_labels <- c(1, 1, 2, 1, 2, 2, 3, 3, 4, 4)
cluster_assignments <- kmeans.result$cluster
BCubed recall and precision
# Calculate BCubed precision
precision <- 0
for (i in unique(true_labels)) {
cluster_indices <- which(true_labels == i)
precision <- precision + sum((table(cluster_assignments[cluster_indices]) * (table(cluster_assignments[cluster_indices]) - 1)) / sum(table(cluster_assignments[cluster_indices])))
}
precision <- precision / sum(table(cluster_assignments))
# Calculate BCubed recall
recall <- 0
for (j in unique(cluster_assignments)) {
cluster_indices <- which(cluster_assignments == j)
recall <- recall + sum((table(true_labels[cluster_indices]) * (table(true_labels[cluster_indices]) - 1)) / sum(table(true_labels[cluster_indices])))
}
recall <- recall / sum(table(true_labels))
cat("BCubed Precision:", precision, "\n")
BCubed Precision: 0.001666667
cat("BCubed Recall:", recall, "\n")
BCubed Recall: 0.1333333
Cluster Analysis for K=5:
Final Analysis: Clusters are more compact than k=2
but less well-separated. Precision and recall remain low, indicating
potential misclassifications.
___________________________________________________________________________________
K=7
Proceeding to k=7, our investigation in k-means clustering expands
further as we explore a configuration with seven clusters. This
adjustment aims to capture even more nuanced patterns and variations
within the dataset. Analyzing the characteristics of the seven clusters
will provide a more granular understanding of the underlying structure,
potentially revealing subtleties that might not be as evident with fewer
clusters. As we delve into k=7, our objective is to refine our
clustering configuration to align more closely with the intricate
details present in the dataset.
# k-means clustering set a seed for random number generation to make the results reproducible
set.seed(8953)
# run kmeans clustering to find 2 clusters
kmeans.result <- kmeans(numeric_data, 7)
# visualize clustering
library(factoextra)
fviz_cluster(kmeans.result, data = numeric_data)

# print the clustering result
print(kmeans.result)
K-means clustering with 7 clusters of sizes 88, 40, 219, 157, 185, 112, 199
Cluster means:
type_school school_accreditation gender interest residence parent_age parent_salary house_area
1 0.7840909 0.5568182 0.4659091 2.2159091 0.9772727 56.95455 0.4076389 0.5278182
2 0.6500000 0.7000000 0.4500000 2.3250000 1.0000000 60.00000 0.3578889 0.5460000
3 0.6894977 0.4840183 0.5342466 3.3972603 0.5342466 50.93151 0.5081735 0.5271826
4 0.6560510 0.5541401 0.4012739 3.8089172 0.8471338 54.07643 0.4766808 0.5529873
5 0.5405405 0.5459459 0.4648649 1.0378378 0.6108108 53.93514 0.4602102 0.5745568
6 0.4821429 0.3750000 0.5803571 2.1785714 0.1160714 46.02679 0.5277877 0.4665714
7 0.5326633 0.3417085 0.4773869 0.3718593 0.1859296 50.34673 0.5340369 0.5831357
parent_was_in_college
1 0.1477273
2 0.0750000
3 0.5159817
4 0.2547771
5 0.4972973
6 0.8482143
7 0.8241206
Clustering vector:
[1] 1 1 3 3 1 6 3 4 7 6 1 7 6 4 3 5 6 3 6 6 3 1 3 2 4 7 4 5 7 7 3 5 2 3 3 4 5 6 5 5 7 5 4 3 3 7 7 4
[49] 5 5 6 1 7 3 7 4 5 6 6 4 7 7 7 6 4 5 7 3 7 4 4 7 6 1 5 4 1 4 7 3 4 7 1 7 1 1 5 3 3 4 5 4 2 3 6 3
[97] 7 4 3 7 7 4 7 3 3 5 3 4 2 1 7 3 3 3 3 6 7 4 2 5 3 7 3 6 1 5 6 3 7 5 4 6 5 3 7 7 4 3 5 4 4 1 6 6
[145] 5 3 7 1 6 7 7 6 1 7 5 3 5 7 5 4 5 3 5 4 7 5 5 2 7 3 5 7 2 7 5 3 3 7 3 7 5 3 5 7 6 5 5 5 4 4 5 6
[193] 7 4 5 4 6 5 5 4 5 1 4 1 3 5 2 6 4 3 6 7 7 4 5 3 5 3 7 3 2 3 1 7 5 7 5 7 4 7 5 5 5 7 7 1 1 7 3 1
[241] 5 3 7 3 4 4 7 7 7 1 2 3 7 4 7 6 3 5 6 4 1 5 7 3 5 7 1 7 4 7 6 7 7 4 7 6 7 5 3 1 7 3 1 3 3 5 5 5
[289] 3 3 1 3 2 3 3 5 7 3 6 5 3 4 6 7 7 7 7 5 6 5 6 4 5 4 5 4 3 5 5 6 2 2 4 6 7 4 6 4 6 4 4 3 3 1 4 5
[337] 6 7 5 7 7 3 3 3 7 6 7 4 3 4 3 7 7 6 5 6 3 1 7 6 3 6 7 7 7 6 3 3 6 1 1 1 2 5 3 3 5 7 4 4 3 5 3 5
[385] 5 3 4 2 7 5 7 1 7 3 3 1 3 7 3 6 4 1 7 4 6 4 5 7 4 3 3 4 3 1 3 5 5 3 5 5 3 7 4 5 1 1 7 1 5 4 7 3
[433] 6 1 4 6 5 6 5 3 5 7 2 1 5 6 7 6 3 4 3 3 7 4 3 4 3 3 4 3 7 1 2 3 3 7 3 3 4 3 4 6 5 4 7 3 7 6 3 7
[481] 5 6 6 7 1 3 4 1 3 7 6 7 3 4 3 4 6 1 6 5 4 4 5 7 5 1 4 7 1 4 6 6 7 1 5 5 1 6 6 5 7 2 1 3 7 3 5 7
[529] 6 6 6 4 3 7 1 4 3 7 6 3 6 7 3 4 7 3 7 7 5 1 7 1 5 4 5 7 3 6 7 3 6 4 4 6 5 3 4 2 3 4 1 4 4 7 3 1
[577] 4 7 7 5 7 7 5 6 5 4 3 3 7 5 6 5 5 4 7 6 5 4 3 4 4 5 3 4 5 3 4 7 4 3 3 7 7 6 2 1 3 7 2 5 3 3 5 6
[625] 1 1 3 3 1 4 7 7 1 7 5 4 3 3 3 2 6 2 7 5 5 1 4 7 3 5 5 5 2 3 3 6 4 5 5 7 2 4 6 2 3 3 6 3 1 7 3 7
[673] 7 4 1 7 5 2 5 5 5 4 6 5 1 2 3 3 3 4 7 3 6 3 5 3 7 5 7 3 4 3 5 6 3 3 3 1 7 4 6 3 1 6 3 5 1 1 7 3
[721] 3 2 4 3 1 1 6 6 5 7 5 3 4 3 6 6 3 4 3 5 6 7 6 7 4 3 5 5 6 5 7 7 5 5 3 7 7 3 5 3 5 5 3 5 4 5 2 1
[769] 7 5 4 4 7 3 3 3 5 6 2 3 7 4 5 5 5 5 5 3 3 5 7 5 5 5 5 3 6 4 5 3 3 7 4 7 4 4 7 4 5 7 6 1 6 7 6 3
[817] 6 7 7 4 4 5 4 3 2 3 7 5 3 2 7 7 5 4 6 7 7 1 7 6 4 7 5 1 1 3 3 5 4 7 3 4 6 4 2 4 6 4 3 7 4 4 5 1
[865] 7 4 2 7 3 5 4 3 6 4 4 3 4 5 3 5 7 1 5 3 2 5 3 5 5 6 6 4 7 4 6 5 4 3 7 7 3 7 4 1 4 7 3 7 2 7 4 5
[913] 3 5 2 5 7 7 4 7 3 5 4 3 7 7 4 4 1 1 6 3 5 4 6 3 4 1 3 5 1 5 4 3 7 3 5 5 4 4 2 3 3 3 7 3 5 5 7 5
[961] 7 3 6 1 1 3 7 7 7 5 4 1 7 5 4 6 4 3 1 3 7 3 3 7 1 5 2 6 5 3 5 3 5 4 3 3 3 7 5 3
Within cluster sum of squares by cluster:
[1] 245.1617 162.4450 689.3797 343.9463 547.8417 769.5869 631.2944
(between_SS / total_SS = 78.9 %)
Available components:
[1] "cluster" "centers" "totss" "withinss" "tot.withinss" "betweenss"
[7] "size" "iter" "ifault"
The Silhouette coefficient
#average for each cluster
avg_sil <- silhouette(kmeans.result$cluster, dist(numeric_data))
#k-means clustering with estimating k and initializations
fviz_silhouette(avg_sil)
NA

The total within-cluster sum of squares
# Calculate total within-cluster sum of squares
total_withinss <- kmeans.result$tot.withinss
cat("Total Within-Cluster Sum of Squares:", sum(total_withinss), "\n")
Total Within-Cluster Sum of Squares: 3389.656
true_labels <- c(1, 1, 2, 1, 2, 2, 3, 3, 4, 4)
cluster_assignments <- kmeans.result$cluster
BCubed recall and precision
# Calculate BCubed precision
precision <- 0
for (i in unique(true_labels)) {
cluster_indices <- which(true_labels == i)
precision <- precision + sum((table(cluster_assignments[cluster_indices]) * (table(cluster_assignments[cluster_indices]) - 1)) / sum(table(cluster_assignments[cluster_indices])))
}
precision <- precision / sum(table(cluster_assignments))
# Calculate BCubed recall
recall <- 0
for (j in unique(cluster_assignments)) {
cluster_indices <- which(cluster_assignments == j)
recall <- recall + sum((table(true_labels[cluster_indices]) * (table(true_labels[cluster_indices]) - 1)) / sum(table(true_labels[cluster_indices])))
}
recall <- recall / sum(table(true_labels))
cat("BCubed Precision:", precision, "\n")
BCubed Precision: 0.0006666667
cat("BCubed Recall:", recall, "\n")
BCubed Recall: 0.06666667
Cluster Analysis for k=7:
Final Analysis: Clusters are less well-separated
than k=2 and k=5 and while more compact than k=5, precision and recall
remain low, indicating potential misclassifications.
___________________________________________________________________________________
Elbow Method Insight:
We implemented the Elbow Method for determining the optimal number of
clusters (k) in a k-means clustering algorithm.
# Function to calculate total within-cluster sum of squares (wss)
wss <- function(k) {
kmeans_result <- kmeans(numeric_data, centers = k, nstart = 10) # You can adjust nstart based on your preference
return(sum(kmeans_result$tot.withinss))
}
# Calculate the total within-cluster sum of squares for different values of k
k_values <- 1:10 # You can adjust the range of k values
wss_values <- sapply(k_values, wss)
# Plot the elbow curve
plot(k_values, wss_values, type = "b", pch = 19, frame = FALSE,
xlab = "Number of Clusters (k)", ylab = "Total Within-Cluster Sum of Squares (WSS)",
main = "Elbow Method")

NA
NA
The elbow method has indicated that K=5 is a reasonable choice in
terms of balancing the trade-off between capturing variance and not
overly complicating the model with too many clusters.
Findings
|
ig |
igr |
gini |
ig |
igr |
gini |
ig |
igr |
gini |
| Accuracy |
84.22% |
85.21% |
85.21% |
82.37 % |
80.34% |
79.66% |
83.84 % |
80.03% |
80.81% |
| Error rate |
15.78% |
14.79% |
14.79% |
17.63% |
19.66% |
20.34% |
16.16% |
19.97% |
19.19% |
| Sensitivity |
89.53% |
87.60% |
88.37% |
75.89% |
77.30% |
78.01% |
86.73% |
84.69% |
76.53% |
| Specificity |
78.71% |
82.73% |
81.93% |
88.31% |
83.12% |
81.17% |
81.00% |
76.00% |
85.00% |
| Precision |
81.34% |
84.01% |
83.52% |
85.60% |
80.74% |
79.14% |
81.73% |
77.57% |
83.33% |
Accuracy (recognition rate): Percentage of test set tuples that are
correctly classified
Error rate (misclassification rate): 1 – accuracy
Sensitivity (recall): True positive recognition rate
Specificity: True negative recognition rate
Precision (exactness): What % of tuples labeled as positive are
actually positive.
For partition 50%-50% : gain ratio and gini index equals in accuracy
85.21% but as we mention before in best model
we choose based on Sensitivity, gini index is the best model with 88.37%
sensitivity.
For partition 70%-30%: information gain is the best model for this
partition with accuracy 82.37 %
For partition 80%-20%: information gain is the best model for this
partition with accuracy 83.84 %
overall 50%-50% wasa the best partition with algorithm gini index
with accuracy 85.21%.
Findings
Classification:
|
ig |
igr |
gini |
ig |
igr |
gini |
ig |
igr |
gini |
| Accuracy |
84.22% |
85.21% |
85.21% |
82.37 % |
80.34% |
79.66% |
83.84 % |
80.03% |
80.81% |
| Error rate |
15.78% |
14.79% |
14.79% |
17.63% |
19.66% |
20.34% |
16.16% |
19.97% |
19.19% |
| Sensitivity |
89.53% |
87.60% |
88.37% |
75.89% |
77.30% |
78.01% |
86.73% |
84.69% |
76.53% |
| Specificity |
78.71% |
82.73% |
81.93% |
88.31% |
83.12% |
81.17% |
81.00% |
76.00% |
85.00% |
| Precision |
81.34% |
84.01% |
83.52% |
85.60% |
80.74% |
79.14% |
81.73% |
77.57% |
83.33% |
Accuracy (recognition rate): Percentage of test set tuples that are
correctly classified
Error rate (misclassification rate): 1 – accuracy
Sensitivity (recall): True positive recognition rate
Specificity: True negative recognition rate
Precision (exactness): What % of tuples labeled as positive are
actually positive.
For partition 50%-50% : gain ratio and gini index equals in accuracy
85.21% but as we mention before in best model
we choose based on Sensitivity, gini index is the best model with 88.37%
sensitivity.
For partition 70%-30%: information gain is the best model for this
partition with accuracy 82.37 %
For partition 80%-20%: information gain is the best model for this
partition with accuracy 83.84 %
overall 50%-50% wasa the best partition with algorithm gini index
with accuracy 85.21%.
Clustering
| Average Silhouette Width |
0.36 |
0.29 |
0.27 |
| Total Within-Cluster Sum of Squares |
8669.059 |
4303.841 |
3389.656 |
| BCubed Precision |
0.002333333 |
0.001666667 |
0.00067 |
| BCubed Recall |
0.1166667 |
0.1333333 |
0.067 |
In our overall analysis, we evaluated the performance of the k-means
clustering algorithm for three different values of k (2, 5, and 7). The
key metrics provide insights into the characteristics of the resulting
clusters:
Average Silhouette Width:
- The average silhouette width measures the separation and cohesion of
clusters. As k increases from 2 to 7, there is a gradual decline in
silhouette width, indicating that clusters become less distinct.
However, k=5 still maintains a reasonable silhouette width, suggesting a
balance between separation and cohesion.
Total Within-Cluster Sum of Squares:
- The total within-cluster sum of squares reflects the compactness of
clusters. Notably, k=7 exhibits the lowest sum of squares, implying more
compact clusters compared to k=5 and k=2. This aligns with the
expectation that a higher k tends to yield more compact clusters.
BCubed Precision and Recall:
- BCubed precision and recall metrics assess the accuracy and
completeness of clustering. Precision and recall values are relatively
low across all k values, indicating potential misclassifications.
However, k=5 stands out with slightly higher precision and recall
compared to k=2 and k=7, suggesting a better balance between accuracy
and completeness.
Considering the results obtained from the elbow method, which
identifies k=5 as the optimal number of clusters, our analysis aligns
with this recommendation. The detailed examination of silhouette width,
within-cluster sum of squares, and BCubed metrics reinforces the choice
of k=5 as a suitable configuration, striking a balance between cluster
distinctiveness and internal cohesion. This comprehensive evaluation
supports the decision to proceed with k=5 for a more refined and
effective clustering solution.
In the following figures, the visualisation of the clusters for each
trial:
K=2:

k=5

K=7

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpJbXBvcnRhbnQgcGFja2FnZXM6DQoNCmBgYHtyfQ0Kc2V0d2QoIkRhdGFzZXQiKQ0KRGF0YXNldCA8LSByZWFkLmNzdigiZGF0YS5jc3YiKQ0KUHJlcHJvY2Vzc2VkX2RhdGFzZXQgPC0gIHJlYWQuY3N2KCJwcmVwcm9jZXNzZWRfZGF0YXNldC5jc3YiKSANCmlmKCFyZXF1aXJlKGdncGxvdDIpKXsNCmluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKX0NCmxpYnJhcnkoZ2dwbG90MikNCmlmKCFyZXF1aXJlKGRwbHlyKSl7DQppbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpfQ0KbGlicmFyeShkcGx5cikgDQoNCmlmKCFyZXF1aXJlKGx0bSkpew0KaW5zdGFsbC5wYWNrYWdlcygibHRtIil9DQpsaWJyYXJ5KGx0bSkNCmlmICghcmVxdWlyZShjbHVzdGVyLCBxdWlldGx5ID0gVFJVRSkpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygiY2x1c3RlciIpDQp9DQppZiAoIXJlcXVpcmUoZmFjdG9leHRyYSwgcXVpZXRseSA9IFRSVUUpKSB7DQogIGluc3RhbGwucGFja2FnZXMoImZhY3RvZXh0cmEiKQ0KfQ0KIA0KaWYgKCFyZXF1aXJlKGNhcmV0LCBxdWlldGx5ID0gVFJVRSkpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQ0KfQ0KaWYgKCFyZXF1aXJlKHBhcnR5LCBxdWlldGx5ID0gVFJVRSkpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygicGFydHkiKQ0KfQ0KaWYgKCFyZXF1aXJlKHBhcnR5a2l0LCBxdWlldGx5ID0gVFJVRSkpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygicGFydHlraXQiKQ0KfQ0KDQppZiAoIXJlcXVpcmUocnBhcnQsIHF1aWV0bHkgPSBUUlVFKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJycGFydCIpDQp9DQppZiAoIXJlcXVpcmUocnBhcnQucGxvdCwgcXVpZXRseSA9IFRSVUUpKSB7DQogIGluc3RhbGwucGFja2FnZXMoInJwYXJ0LnBsb3QiKQ0KfQ0KaWYgKCFyZXF1aXJlKHRpZHl2ZXJzZSwgcXVpZXRseSA9IFRSVUUpKSB7DQogIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQp9DQoNCmlmICghcmVxdWlyZShSV2VrYSwgcXVpZXRseSA9IFRSVUUpKSB7DQogIGluc3RhbGwucGFja2FnZXMoIlJXZWthIikNCn0NCmBgYA0KDQojIElUMzI2IFByb2plY3Qge3N0eWxlPSJjb2xvcjogZ3JheSJ9DQoNClxfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cXw0KDQojIyBUaGUgR29hbA0KDQpPdXIgcHJpbWFyeSBvYmplY3RpdmUgb2YgdGhpcyBhbmFseXNpcyBpcyB0byBjbGFzc2lmeSB3aGF0ZXZlciBhIHN0dWRlbnQgd2lsbCBnbyB0byBjb2xsZWdlIG9yIG5vdCB1c2luZyB0aGUgY2xhc3NpZmljYXRpb24gbWV0aG9kcyBhbmQgdG8gaWRlbnRpZnkgdGhlIG1haW4gZmFjdG9ycyBhbmQgcmVhc29ucyB3aHkgc3R1ZGVudHMgYXJlIGxlc3MgbGlrZWx5IHRvIHB1cnN1ZSBoaWdoZXIgZWR1Y2F0aW9uIGluZGljYXRlZCBieSAid2lsbF9nb190b19jb2xsZWdlIiBiZWluZyAnRmFsc2UnLiBCeSBsZXZlcmFnaW5nIHRoZSBwcm92aWRlZCBkYXRhc2V0IHdpdGggYXR0cmlidXRlcyBzdWNoIGFzIHNjaG9vbCB0eXBlLCBzY2hvb2wgYWNjcmVkaXRhdGlvbiwgZ2VuZGVyLCBpbnRlcmVzdCBpbiBjb2xsZWdlLCByZXNpZGVuY2UsIHdlIGFpbSB0byBkaXNjb3ZlciB0aGUgbW9zdCBpbmZsdWVudGlhbCB2YXJpYWJsZXMgYW5kIHRoZWlyIHJlbGF0aW9uc2hpcHMgd2l0aCB0aGUgZGVjaXNpb24gbm90IHRvIGF0dGVuZCBjb2xsZWdlLg0KDQojIyBUaGUgU291cmNlDQoNCkthZ2dsZS5jb20NCg0KIyMgVVJMIDoNCg0KPGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZGF0YXNldHMvc2FkZGFtYXp5YXp5L2dvLXRvLWNvbGxlZ2UtZGF0YXNldD4NCg0KIyMgR2VuZXJhbCBpbmZvcm1hdGlvbg0KDQotICAgTnVtYmVyIG9mIGF0dHJpYnV0ZXMgOiAxMQ0KLSAgIE51bWJlciBvZiByb3dzIChvYmplY3RzKSA6IDEwMDANCi0gICBUaGUgY2xhc3MgbGFiZWw6IFRoZSBjbGFzcyBsYWJlbCBpbiBvdXIgcHJvamVjdCBpcyB0aGUgYXR0cmlidXRlICJ3aWxsX2dvX3RvX2NvbGxlZ2UiLiBUaGlzIGF0dHJpYnV0ZSBpcyBiaW5hcnksIG1lYW5pbmcgdGhhdCBpdCBjYW4gdGFrZSBvbiB0d28gdmFsdWVzOiBUcnVlIGZvciB5ZXMgb3IgRmFsc2UgZm9yIG5vLiBUaGUgdmFsdWUgb2YgdGhpcyBhdHRyaWJ1dGUgd2lsbCBiZSB0aGUgdGFyZ2V0IHZhcmlhYmxlIHRoYXQgd2UgYXJlIHRyeWluZyB0byBwcmVkaWN0IGR1cmluZyBvdXIgcHJvamVjdC4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBBdHRyaWJ1dGUgICAgICAgICAgICAgfCBEZXNjcmlwdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgVHlwZSAgICAgIHwgUG9zc2libGUgdmFsdWVzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgdHlwZV9zY2hvb2wgICAgICAgICAgIHwgVGhlIHR5cGUgb2Ygc2Nob29sIHRoZSBzdHVkZW50IGF0dGVuZHMgICAgICAgICAgICAgICAgICAgICB8IEJpbmFyeSAgICB8IEFjYWRlbWljIC8gVm9jYXRpb25hbCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IHNjaG9vbF9hY2NyZWRpdGF0aW9uICB8IFRoZSBxdWFsaXR5IGlmIHNjaG9vbCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBCaW5hcnkgICAgfCBBIC8gQiAoQSBpcyBiZXR0ZXIgdGhhbiBCKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBnZW5kZXIgICAgICAgICAgICAgICAgfCBUaGUgc3R1ZGVudCdzIGdlbmRlciAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgQmluYXJ5ICAgIHwgTWFsZSAvIEZlbWFsZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgaW50ZXJlc3QgICAgICAgICAgICAgIHwgVGhlIHN0dWRlbnQncyBpbnRlcmVzdCBpbiBnb2luZyB0byBjb2xsZWdlICAgICAgICAgICAgICAgICB8IE5vbWluYWwgICB8IFZlcnkgaW50ZXJlc3RlZCAvSW50ZXJlc3RlZCAvIExlc3MgSW50ZXJlc3RlZCAvIE5vdCBJbnRlcmVzdGVkIC9VbmNlcnRhaW4gfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IHJlc2lkZW5jZSAgICAgICAgICAgICB8IFRoZSBzdHVkZW50J3MgcmVzaWRlbmNlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBCaW5hcnkgICAgfCBVcmJhbiAvIFJ1cmFsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBwYXJlbnRfYWdlICAgICAgICAgICAgfCBUaGUgYWdlIG9mIHRoZSBzdHVkZW50J3MgcGFyZW50cyAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgTnVtZXJpYyAgIHwgNDAgLSA2NSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgcGFyZW50X3NhbGFyeSAgICAgICAgIHwgVGhlIG1vbnRobHkgc2FsYXJ5IG9mIHRoZSBzdHVkZW50J3MgcGFyZW50cyBpbiBJRFIvUnVwaWFoLiB8IE51bWVyaWMgICB8IDEwMDBLIC0gMTBNICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICB8IFsxUnVwaWFoID0gMC4wMDAyNFNBUl0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBob3VzZV9hcmVhICAgICAgICAgICAgfCBUaGUgc2l6ZSBvZiB0aGUgc3R1ZGVudCdzIGhvdXNlIGluIG1ldGVyIHNxdWFyZSAgICAgICAgICAgIHwgTnVtZXJpYyAgIHwgMjAgLSAxMjAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYXZlcmFnZV9ncmFkZXMgICAgICAgIHwgVGhlIHN0dWRlbnQncyBhdmVyYWdlIGdyYWRlcyBpbiBzY2hvb2wuICAgICAgICAgICAgICAgICAgICB8IE51bWVyaWMgICB8IDc1IC0gOTggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IHBhcmVudF93YXNfaW5fY29sbGVnZSB8IFdoZXRoZXIgdGhlIHN0dWRlbnQncyBwYXJlbnRzIGF0dGVuZGVkIGNvbGxlZ2UuICAgICAgICAgICAgfCBCaW5hcnkgICAgfCBUcnVlIC0gRmFsc2UgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCB3aWxsX2dvX3RvX2NvbGxlZ2UgICAgfCBXaGV0aGVyIHRoZSBzdHVkZW50IHBsYW5zIHRvIGdvIHRvIGNvbGxlZ2UuICAgICAgICAgICAgICAgIHwgQmluYXJ5ICAgIHwgVHJ1ZSAtIEZhbHNlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCg0KIyMgU2FtcGxlIG9mIG91ciBkYXRhDQoNCmBgYHtyfQ0KaGVhZChEYXRhc2V0KQ0KYGBgDQoNCkhlcmUgYXJlIGEgc2FtcGxlIG9mIDYgcm93IGZyb20gb3VyIGRhdGFzZXQuDQoNCiMjIE1pc3NpbmcgdmFsdWVzDQoNCmBgYHtyfQ0Kc3VtKGlzLm5hKERhdGFzZXQpKQ0KYGBgDQoNClRoZXJlIGFyZSBubyBtaXNzaW5nIHZhbHVlcyBpbiBvdXIgZGF0YXNldC4NCg0KIyMgU3RhdGlzdGljYWwgZ3JhcGhzDQoNCkdyYXBoIDE6DQoNCmBgYHtyfQ0KZGYgPWRhdGEuZnJhbWUoRGF0YXNldCkNCmdncGxvdChkYXRhPWRmLCBhZXMoeCA9IGludGVyZXN0LCBmaWxsID0gd2lsbF9nb190b19jb2xsZWdlKSkgKw0KICBnZW9tX2JhcigpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBjKCdOb3QgSW50ZXJlc3RlZCcsICdMZXNzIEludGVyZXN0ZWQnLCAnVW5jZXJ0YWluJywgJ0ludGVyZXN0ZWQnLCAnVmVyeSBJbnRlcmVzdGVkJykpICsNCiAgbGFicyh0aXRsZSA9ICdDb2xsZWdlIGludGVyZXN0IHZzIENvbGxlZ2UgYXR0ZW5kYW5jZSAnKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIlRydWUiID0gImFudGlxdWV3aGl0ZTIiLCAiRmFsc2UiID0gImFudGlxdWV3aGl0ZTMiKSkgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KDQpgYGANCg0KQWNjb3JkaW5nIHRvIHRoZSBncmFwaCwgd2hldGhlciBzdHVkZW50cyBhcmUgaW50ZXJlc3RlZCBpbiBnb2luZyB0byBjb2xsZWdlIG9yIG5vdCBkb2VzIG5vdCBhZmZlY3Qgd2hldGhlciB0aGV5IGFjdHVhbGx5IGVuZCB1cCBhdHRlbmRpbmcgQ29sbGFnZSAuIFRoZXJlIGlzIGEgZ3JvdXAgb2YgaW5kaXZpZHVhbHMgd2hvIHdlcmUgaW50ZXJlc3RlZCBpbiBhdHRlbmRpbmcgYnV0IGRpZCBub3QgcmVjZWl2ZSBhY2NlcHRhbmNlLCB3aGlsZSBvdGhlcnMgd2hvIHdlcmUgbm90IGludGVyZXN0ZWQgd2VyZSBhY2NlcHRlZC4NCg0KR3JhcGggMjoNCg0KYGBge3J9DQoNCmZpbHRlcmVkX1RydWUgPWZpbHRlcihEYXRhc2V0LCB3aWxsX2dvX3RvX2NvbGxlZ2UgPT0gJ1RydWUnKQ0KZmlsdGVyZWRfRmFsc2UgPXN1YnNldChEYXRhc2V0LCB3aWxsX2dvX3RvX2NvbGxlZ2UgPT0nRmFsc2UnKQ0KDQpnZ3Bsb3QoKSArDQogIGdlb21fZGVuc2l0eShkYXRhID0gZmlsdGVyZWRfVHJ1ZSwgYWVzKHggPSBhdmVyYWdlX2dyYWRlcywgZmlsbCA9ICJHb2luZyB0byBDb2xsZWdlIiksIGFscGhhID0gMC41KSArDQogIGdlb21fZGVuc2l0eShkYXRhID0gZmlsdGVyZWRfRmFsc2UsIGFlcyh4ID0gYXZlcmFnZV9ncmFkZXMsIGZpbGwgPSAiTk9UIEdvaW5nIHRvIENvbGxlZ2UiKSwgYWxwaGEgPSAwLjUpICsNCiAgbGFicyh4ID0gIkF2ZXJhZ2UgR3JhZGVzIiwgeSA9ICJEZW5zaXR5IikgKw0KICBnZ3RpdGxlKCJDb21wYXJpc29uIG9mIEF2ZXJhZ2UgR3JhZGVzIGZvciBTdHVkZW50cyBHb2luZyB0byBDb2xsZWdlIGFuZCBOT1QgR29pbmcgdG8gQ29sbGVnZSIpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiR29pbmcgdG8gQ29sbGVnZSIgPSAiYW50aXF1ZXdoaXRlNCIsICJOT1QgR29pbmcgdG8gQ29sbGVnZSIgPSAiYW50aXF1ZXdoaXRlMSIpKQ0KDQpgYGANCg0KVGhlIGdyYXBoIHNob3dzIHRoYXQgdGhlIGF2ZXJhZ2UgZ3JhZGVzIGZvciBzdHVkZW50cyB3aG8gaGFkIGFjY2VwdGVkIHRvIGdvIHRvIGNvbGxlZ2Ugd2VyZSBoaWdoZXIgdGhhbiB0aG9zZSB3aG8gZGlkIG5vdCBlbnRlciBjb2xsZWdlICwgYW5kIHRoaXMgaW5kaWNhdGVzIHRoZSBleGlzdGVuY2Ugb2YgYSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRob3NlIHdobyBnb2luZyB0byBjb2xsZWdlIGFuZCB0aGUgYXZlcmFnZSBncmFkZXMNCg0KR3JhcGggMzoNCg0KYGBge3J9DQpEYXRhc2V0X3BlcmNlbnRhZ2UgPC0gRGF0YXNldCAlPiUNCiAgZ3JvdXBfYnkodHlwZV9zY2hvb2wpICU+JQ0KICBzdW1tYXJpc2UocGVyY2VudGFnZSA9IG1lYW4od2lsbF9nb190b19jb2xsZWdlID09ICJUcnVlIikgKiAxMDApDQoNCiMgQ3JlYXRlIGEgcGVyY2VudGFnZSBjaGFydA0KZ2dwbG90KERhdGFzZXRfcGVyY2VudGFnZSwgYWVzKHggPSB0eXBlX3NjaG9vbCwgeSA9IHBlcmNlbnRhZ2UsIGZpbGwgPSB0eXBlX3NjaG9vbCkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgbGFicyh0aXRsZSA9ICJQZXJjZW50YWdlIG9mIFN0dWRlbnRzIEdvaW5nIHRvIENvbGxlZ2UgYnkgVHlwZSBvZiBTY2hvb2wiLA0KICAgICAgIHggPSAiVHlwZSBvZiBTY2hvb2wiLA0KICAgICAgIHkgPSAiUGVyY2VudGFnZSIpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiQWNhZGVtaWMiID0gImFudGlxdWV3aGl0ZTMiLCAiVm9jYXRpb25hbCIgPSAiYW50aXF1ZXdoaXRlMiIpKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQogDQpgYGANCg0KVGhpcyBncmFwaCBzaG93cyB0aGUgaW1wYWN0IG9mIHRoZSB0eXBlIG9mIGhpZ2ggc2Nob29sIGF0dGVuZGVkIGJ5IHN0dWRlbnRzIG9uIHRoZWlyIGNvbGxlZ2UgYXR0ZW5kYW5jZSAuIEJhc2VkIG9uIHRoZSBiYXIgY2hhcnQ6DQoNCi0gICBhbW9uZyBzdHVkZW50cyBmcm9tIEFjYWRlbWljIGhpZ2ggc2Nob29scywgMzEzIGFyZSBnb2luZyB0byBjb2xsZWdlLCBhbmQgMjk2IGFyZSBub3QuDQoNCi0gICBhbW9uZyBzdHVkZW50cyBmcm9tIFZvY2F0aW9uYWwgaGlnaCBzY2hvb2xzLCAxODcgYXJlIGdvaW5nIHRvIGNvbGxlZ2UsIGFuZCAyMDQgYXJlIG5vdA0KDQpUaGVzZSBpbmZvcm1hdGlvbiB0ZWxsIHVzIHRoYXQgYSBoaWdoZXIgcHJvcG9ydGlvbiBvZiBzdHVkZW50cyBmcm9tIGFjYWRlbWljIGhpZ2ggc2Nob29scyBhcmUgZ29pbmcgdG8gY29sbGVnZSBjb21wYXJlZCB0byB0aG9zZSBmcm9tIHZvY2F0aW9uYWwgc2Nob29scyB3aGljaCBzdWdnZXN0IHRoYXQgdGhlIHR5cGUgb2Ygc2Nob29sIGF0dGVuZGVkLg0KDQpHcmFwaCA0Og0KDQpgYGB7cn0NCmdncGxvdChEYXRhc2V0LCBhZXMoeCA9IGF2ZXJhZ2VfZ3JhZGVzKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDUsIGZpbGwgPSAiYW50aXF1ZXdoaXRlMiIsIGNvbG9yID0gImFudGlxdWV3aGl0ZTQiKSArDQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIHN0dWRlbnRzJyBncmFkZXMiLA0KICAgICAgIHggPSAiU3R1ZGVudHMnIGF2ZXJhZ2UgZ3JhZGVzIiwNCiAgICAgICB5ID0gIkZyZXF1ZW5jeSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KVGhpcyBoaXN0b2dyYW0gc2hvdyB1cyB0aGF0IHRoZSBtYWpvcml0eSBvZiB0aGUgc3R1ZGVudHMgaW4gdGhlIGRhdGFzZXQgYXJlIHBlcmZvcm1pbmcgd2VsbCBzaW5jZSBpdCBzZWVtcyBsaWtlIHRoZWlyIGdyYWRlcyBhcmUgc3Bhbm5pbmcgYmV0d2VlbiA3NSBhbmQgOTguIFRoaXMgYW5hbHlzaXMgd2lsbCBoZWxwIHVzIGRldGVybWluZSB3aGV0aGVyIHRoZSBhY2FkZW1pYyBwZXJmb3JtYW5jZSBsZXZlbCBvZiBzdHVkZW50cyBpcyBhIGNvbnRyaWJ1dGluZyBmYWN0b3IgdG8gdGhlaXIgY29sbGVnZSBhdHRlbmRhbmNlIG9yIG5vdC4NCg0KIyMgU3RhdGlzdGljYWwgTWVhc3VyZXMNCg0KMS4gIFRoZSBzdHVkZW50J3MgYWNhZGVtaWMgcGVyZm9ybWFuY2UgYW5hbHlzaXM6DQoNCmBgYHtyfQ0Kc3VtbWFyeShEYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzKQ0KDQpgYGANCg0KVGhlIHN0dWRlbnQgZ3JhZGVzIGluIG91ciBkYXRhc2V0IHJhbmdlIGZyb20gNzUuMDAgdG8gOTguMDAsIHdpdGggYSBtZWRpYW4gb2YgODUuNTggYW5kIGFuIGF2ZXJhZ2Ugb2YgODYuMTAuIFRoaXMgc3VnZ2VzdHMgdGhhdCBtb3N0IHN0dWRlbnRzIGFyZSBkb2luZyB3ZWxsIGFzIG5vbmUgb2YgdGhlbSBoYXZlIGF2ZXJhZ2UgZ3JhZGVzIGJlbG93IDUwLiBIb3dldmVyLCBpdCdzIGludGVyZXN0aW5nIHRvIG5vdGUgdGhhdCBzb21lIHN0dWRlbnRzIGhhdmUgbXVjaCBoaWdoZXIgb3IgbG93ZXIgZ3JhZGVzIHRoYW4gdGhlIGF2ZXJhZ2UsIG1haW5seSBkdWUgdG8gdGhlIHdpZGUgcmFuZ2Ugb2YgZ3JhZGVzLi4NCg0KMi4gIFRoZSBzb2Npb2Vjb25vbWljIHN0YXR1cyBvZiBzdHVkZW50cycgZmFtaWxpZXMgYW5hbHlzaXM6DQoNCmBgYHtyfQ0Kc3VtbWFyeShEYXRhc2V0JHBhcmVudF9zYWxhcnkpDQpgYGANCg0KSW4gdGhlIGRhdGFzZXQsIHdlJ3ZlIGdvdCBzdHVkZW50cyBwYXJlbnRzIHdpdGggc2FsYXJpZXMgcmFuZ2luZyBmcm9tIDEsMDAwLDAwMCB0byAxMCwwMDAsMDAwIElEUi9SdXBpYWguIFRoZSBtZWRpYW4gc2FsYXJ5IGlzIDUsNDQwLDAwMCBJRFIvUnVwaWFoLCBhbmQgdGhlIGF2ZXJhZ2UgaXMgNSwzODEsNTcwIElEUi9SdXBpYWguIFRoaXMgZGF0YSB0ZWxscyB1cyB0aGF0IG1hbnkgcGFyZW50cyBpbiBvdXIgZGF0YXNldCBlYXJuIGxlc3MgdGhhbiB0aGUgYXZlcmFnZSBzYWxhcnkgaW4gSW5kb25lc2lhLCB3aGljaCBpcyAxNDYsMDAwLDAwMCBJRFIuIFRoaXMgc3VnZ2VzdHMgdGhhdCBxdWl0ZSBhIGZldyBzdHVkZW50cyBpbiBvdXIgZGF0YXNldCBjb21lIGZyb20gZmFtaWxpZXMgd2l0aCBsaW1pdGVkIGZpbmFuY2VzLiBBbmQgdGhpcyBmaW5hbmNpYWwgc2l0dWF0aW9uIGNvdWxkIGNlcnRhaW5seSBpbXBhY3QgdGhlaXIgYWJpbGl0eSB0byBnZXQgdGhyb3VnaCBjb2xsZWdlLg0KDQpgYGB7cn0NCnN1bW1hcnkoRGF0YXNldCRob3VzZV9hcmVhKQ0KYGBgDQoNCkFkZGl0aW9uYWxseSB3ZSBjYW4gdXRpbGl6ZSB0aGUgaG91c2UgYXJlYSBhdHRyaWJ1dGUgdG8gZ2FpbiBhIGRlZXBlciB1bmRlcnN0YW5kaW5nIG9mIHRoZSBzb2Npb2Vjb25vbWljIHN0YXR1cyBvZiBzdHVkZW50cycgZmFtaWxpZXMsIHdoZXJlIHN0dWRlbnRzIHdpdGggaG91c2VzIHNpZ25pZmljYW50bHkgbGFyZ2VyIHRoYW4gdGhlIG1lYW4gbWlnaHQgaW5kaWNhdGUgYSBoaWdoZXIgc29jaW9lY29ub21pYyBzdGF0dXMsIHdoaWxlIHRob3NlIHdpdGggaG91c2VzIGNvbnNpZGVyYWJseSBzbWFsbGVyIHRoYW4gdGhlIG1lYW4gbWlnaHQgcmVmbGVjdCBhIGNvbXBhcmF0aXZlbHkgbG93ZXIgc29jaW9lY29ub21pYyBzdGF0dXMuIEJhc2VkIG9uIHRoZSBzaG93biBvdXRwdXQsIHRoZSBob3VzZSBhcmVhcyByYW5nZSBmcm9tIFsyMC4wMC0xMjAuMDAg446hXS4gVGhlIG1lZGlhbiBob3VzZSBhcmVhIGlzIDc1LjUwIOOOoSBpbmRpY2F0ZXMgdGhhdCBmYW1pbGllcyB3aXRoIGhvdXNlIGFyZWFzIGFyb3VuZCB0aGlzIHZhbHVlIGxpa2VseSBoYXZlIG1vZGVyYXRlIHNvY2lvZWNvbm9taWMgc3RhdHVzIHdpdGggaG91c2VzIHRoYXQgbmVpdGhlciB2ZXJ5IHNtYWxsIG5vciB2ZXJ5IGxhcmdlLg0KDQozLiAgVW5kZXJzdGFuZGluZyBQYXJlbnQgQWdlIFJhbmdlIGFuZCBWYXJpYXRpb24gaW4gaXRzIFZhbHVlcw0KDQpgYGB7cn0NCnN1bW1hcnkoRGF0YXNldCRwYXJlbnRfYWdlKQ0KU0Q9c2QoRGF0YXNldCRwYXJlbnRfYWdlKQ0KTWVhbkFnZT1tZWFuKERhdGFzZXQkcGFyZW50X2FnZSkNCmNhdCgiY29lZmZpY2llbnQgb2YgdmFyaWF0aW9uOiIsU0QvTWVhbkFnZSoxMDAsIiUiKQ0KDQpgYGANCg0KVGhpcyBzdW1tYXJ5IHByb3ZpZGVzIHRoZSByYW5nZSBmb3IgYWdlIGF0dHJpYnV0ZSBbNDAsNjVdIHdoaWNoIGluZGljYXRlcyB0aGF0IGFsbCBwYXJlbnQgaW4gbWlkZGxlIGFnZSBkdXJpbmcgdGhpcyBhZ2UgcGFyZW50IGhhdmUgbW9yZSBjb25jZXJuIGFib3V0IHRoZWlyIGNoaWxkcmVuICwgdGhlIGNvZWZmaWNpZW50IG9mIHZhcmlhdGlvbj0gNi43JSB3aGljaCBpbmRpY2F0ZXMgbG93ZXIgdmFyaWF0aW9uICxhbmQgdGhlIHZhbHVlIG9mIGF0dHJpYnV0ZSBwYXJlbnRfYWdlcmFyZSBhcmUgcmVsYXRpdmVseSBjbG9zZSB0byB0aGUgbWVhbiBvdmVyYWxsIDI1JSBvZiB0aGVtIGhhdmUgYW4gYWdlIGJlbG93IG9yIGVxdWFsIHRvIDUwICwgNzUlIGhhdmUgYW4gYWdlIGJlbG93IG9yIGVxdWFsIHRvIDU0IGFuZCB0aGUgbWVkaWFuIHZhbHVlIGlzIDUyDQoNCiMjIE91dGxpZXJzIGFuYWx5c2lzDQoNCmBgYHtyfQ0KIyMjcGFyZW50IGFnZSBvdXRsaWVycw0KcXVhcnRpbGVzIDwtIHF1YW50aWxlKERhdGFzZXQkcGFyZW50X2FnZSwgcHJvYnM9YyguMjUsIC43NSksIG5hLnJtID0gRkFMU0UpDQpJUVIgPC0gSVFSKERhdGFzZXQkcGFyZW50X2FnZSkNCg0KTG93ZXIgPC0gcXVhcnRpbGVzWzFdIC0gMS41KklRUg0KVXBwZXIgPC0gcXVhcnRpbGVzWzJdICsgMS41KklRUiANCg0KZGF0YV9ub19vdXRsaWVyIDwtIHN1YnNldChEYXRhc2V0LCBEYXRhc2V0JHBhcmVudF9hZ2UgPiBMb3dlciAmIERhdGFzZXQkcGFyZW50X2FnZSA8IFVwcGVyKQ0KZGltKGRhdGFfbm9fb3V0bGllcikNCg0KIyMjcGFyZW50IHNhbGFyeSBvdXRsaWVycw0KcXVhcnRpbGVzIDwtIHF1YW50aWxlKERhdGFzZXQkcGFyZW50X3NhbGFyeSwgcHJvYnM9YyguMjUsIC43NSksIG5hLnJtID0gRkFMU0UpDQpJUVIgPC0gSVFSKERhdGFzZXQkcGFyZW50X3NhbGFyeSkNCg0KTG93ZXIgPC0gcXVhcnRpbGVzWzFdIC0gMS41KklRUg0KVXBwZXIgPC0gcXVhcnRpbGVzWzJdICsgMS41KklRUiANCg0KZGF0YV9ub19vdXRsaWVyIDwtIHN1YnNldChkYXRhX25vX291dGxpZXIsIGRhdGFfbm9fb3V0bGllciRwYXJlbnRfc2FsYXJ5PiBMb3dlciAmIGRhdGFfbm9fb3V0bGllciRwYXJlbnRfc2FsYXJ5IDwgVXBwZXIpDQpkaW0oZGF0YV9ub19vdXRsaWVyKQ0KDQoNCiMjI2F2ZXJnZSBncmFkZXMgb3V0bGllcnMNCnF1YXJ0aWxlcyA8LSBxdWFudGlsZShEYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzLCBwcm9icz1jKC4yNSwgLjc1KSwgbmEucm0gPSBGQUxTRSkNCklRUiA8LSBJUVIoRGF0YXNldCRhdmVyYWdlX2dyYWRlcykNCg0KTG93ZXIgPC0gcXVhcnRpbGVzWzFdIC0gMS41KklRUg0KVXBwZXIgPC0gcXVhcnRpbGVzWzJdICsgMS41KklRUiANCg0KZGF0YV9ub19vdXRsaWVyIDwtIHN1YnNldChkYXRhX25vX291dGxpZXIsIGRhdGFfbm9fb3V0bGllciRhdmVyYWdlX2dyYWRlcz4gTG93ZXIgJiBkYXRhX25vX291dGxpZXIkYXZlcmFnZV9ncmFkZXMgPCBVcHBlcikNCmRpbShkYXRhX25vX291dGxpZXIpDQoNCg0KIyMjaG91c2UgYXJlYSBvdXRsaWVycw0KcXVhcnRpbGVzIDwtIHF1YW50aWxlKERhdGFzZXQkaG91c2VfYXJlYSwgcHJvYnM9YyguMjUsIC43NSksIG5hLnJtID0gRkFMU0UpDQpJUVIgPC0gSVFSKERhdGFzZXQkaG91c2VfYXJlYSkNCg0KTG93ZXIgPC0gcXVhcnRpbGVzWzFdIC0gMS41KklRUg0KVXBwZXIgPC0gcXVhcnRpbGVzWzJdICsgMS41KklRUiANCg0KZGF0YV9ub19vdXRsaWVyIDwtIHN1YnNldChkYXRhX25vX291dGxpZXIsIGRhdGFfbm9fb3V0bGllciRob3VzZV9hcmVhPiBMb3dlciAmIGRhdGFfbm9fb3V0bGllciRob3VzZV9hcmVhIDwgVXBwZXIpDQoNCkZvdW5kZWRfT3V0bGllcnM9ZGF0YS5mcmFtZShhbnRpX2pvaW4oRGF0YXNldCxkYXRhX25vX291dGxpZXIpKQ0KcHJpbnQoRm91bmRlZF9PdXRsaWVycykNCmBgYA0KDQpBZnRlciBjb25kdWN0aW5nIGRhdGEgYW5hbHlzaXMgYW5kIGlkZW50aWZ5aW5nIG91dGxpZXJzLCBvdXIgaW5zcGVjdGlvbiByZXZlYWxzIHRoYXQgdGhlIGRldGVjdGVkIG91dGxpZXJzIHJlcHJlc2VudCBpbmhlcmVudCB2YXJpYXRpb24gd2l0aGluIHRoZSBwb3B1bGF0aW9uLiBSZWdhcmRpbmcgUGFyZW50X2FnZSwgb3V0bGllcnMgYXJlIG9ic2VydmVkIGZvciB2YWx1ZXMgYmVsb3cgNDQgYW5kIGFib3ZlIDY1LiBIb3dldmVyLCBpdCBzaG91bGQgYmUgbm90ZWQgdGhlIGFnZSBmcm9tIDQwIHRvIDY1IGZhbGwgd2l0aGluIHRoZSBleHBlY3RlZCBtZWFuIG9mIG91ciBkYXRhc2V0IG1lYW5pbmcgdGhhdCBpdCBkb2Vzbid0IGluZGljYXRlIHRoYXQgdGhleSBhcmUgb3V0bGllcnMgLiBGb3IgcGFyZW50X3NhbGFyeSwgd2UgZm91bmQgdHdvIG91dGxpZXJzOiBvbmUgYmVsb3cgMSwzMjYsMjUwIGluZCDiiYggODUgVVNEIGFuZCBhbm90aGVyIGFib3ZlIDksNDE2LDI1MCBpbmQg4omIIDYwNiBVU0QuIFRoZSBtaW5pbXVtIGFuZCBtYXhpbXVtIHZhbHVlcyB3ZXJlIGRldGVybWluZWQgdG8gYmUgMSwwMDAsMDAwIGluZCDiiYggNjQgVVNEIGFuZCAxMCwwMDAsMDAwIGluZCDiiYggNjQ0IFVTRCwgcmVzcGVjdGl2ZWx5LiBJbiB0aGUgY2FzZSBvZiBncmFkZXMsIHR3ZWx2ZSBvdXRsaWVycyB3ZXJlIGlkZW50aWZpZWQsIHJhbmdpbmcgZnJvbSBiZWxvdyA3NiB0byBhYm92ZSA5Ny4gTmV2ZXJ0aGVsZXNzLCBzaW5jZSB0aGUgZGF0YSBmYWxscyB3aXRoaW4gdGhlIGFjY2VwdGFibGUgcmFuZ2Ugb2YgMCB0byAxMDAsIHRoZXNlIG91dGxpZXJzIHNob3VsZCBiZSByZXRhaW5lZCBhcyB0aGV5IGFyZSBzdGlsbCBjb25zaWRlcmVkIG5vcm1hbCBhbmQgd2l0aGluIHRoZSB1c3VhbCBncmFkZSByYW5nZS4gRmluYWxseSwgZm9yIGhvdXNlX2FyZWEsIHdlIGZvdW5kIGVsZXZlbiBvdXRsaWVycyBiZWxvdyAzNC40bSBhbmQgYWJvdmUgMTE1bSwgd2l0aCB0aGUgbWluaW11bSBiZWluZyAyMG0gYW5kIHRoZSBtYXhpbXVtIGJlaW5nIDEyMG0uIEhvd2V2ZXIsIHRoZXNlIHZhbHVlcyBhcmUgc3RpbGwgY29uc2lkZXJlZCB0eXBpY2FsIGZvciB0aGUgcG9wdWxhdGlvbi4NCg0KIyMgTm9ybWFsaXphdGlvbg0KDQpgYGB7cn0NCm5vcm1hbGl6ZSA8LSBmdW5jdGlvbih4KSB7cmV0dXJuKCh4LW1pbih4KSkvIChtYXgoeCktbWluKHgpKSl9DQpkYXRhc2V0V2l0aG91dE5vcm1hbGl6YXRpb248LURhdGFzZXQNCkRhdGFzZXQkcGFyZW50X3NhbGFyeTwtbm9ybWFsaXplKGRhdGFzZXRXaXRob3V0Tm9ybWFsaXphdGlvbiRwYXJlbnRfc2FsYXJ5KQ0KRGF0YXNldCRob3VzZV9hcmVhPC1ub3JtYWxpemUoZGF0YXNldFdpdGhvdXROb3JtYWxpemF0aW9uJGhvdXNlX2FyZWEpDQpwcmludChEYXRhc2V0KQ0KYGBgDQoNCldlIGFwcGxpZWQgbm9ybWFsaXphdGlvbiB0byB0aGUgJ3BhcmVudF9zYWxhcnknIGFuZCAnaG91c2VfYXJlYScgYXR0cmlidXRlcywgc2NhbGluZyB0aGVpciB2YWx1ZXMgdG8gYSByYW5nZSBiZXR3ZWVuIDAgYW5kIDEuIFRoaXMgbm9ybWFsaXphdGlvbiBwcm9jZXNzIGdyZWF0bHkgZmFjaWxpdGF0ZXMgZGF0YSBoYW5kbGluZyBhbmQgYW5hbHlzaXMsIGVuc3VyaW5nIHRoYXQgdGhlc2UgYXR0cmlidXRlcyBhcmUgb24gYSBjb25zaXN0ZW50IHNjYWxlLiBXaGljaCB3aWxsIGltcHJvdmUgdGhlIHJlbGlhYmlsaXR5IG9mIG91ciBkYXRhIGFuYWx5c2lzIGFuZCBlbmFibGUgYmV0dGVyIGNvbmNsdXNpb25zIHRvIGJlIGRyYXduIGZyb20gdGhlIGRhdGFzZXQuIE5vcm1hbGl6YXRpb24gaXMgYSBjcnVjaWFsIHN0ZXAgaW4gcHJlcGFyaW5nIHRoZSBkYXRhIGZvciBtb2RlbGluZywgYXMgaXQgcHJldmVudHMgYXR0cmlidXRlcyB3aXRoIGxhcmdlciBudW1lcmljYWwgcmFuZ2VzIGZyb20gZG9taW5hdGluZyB0aGUgYW5hbHlzaXMgYW5kIGVuc3VyZXMgZmFpciB0cmVhdG1lbnQgZm9yIGFsbCBmZWF0dXJlcy4NCg0KIyMgRGlzY3JldGl6YXRpb24NCg0KYGBge3J9DQoNCg0KRGF0YXNldCRhdmVyYWdlX2dyYWRlcyBbRGF0YXNldCRhdmVyYWdlX2dyYWRlcyA+PSA5NV0gPC0gJytBJw0KRGF0YXNldCRhdmVyYWdlX2dyYWRlcyBbOTUgPkRhdGFzZXQkYXZlcmFnZV9ncmFkZXMgJiBEYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzID49IDkwXSA8LSAnQScNCkRhdGFzZXQkYXZlcmFnZV9ncmFkZXMgWzkwID5EYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzICYgRGF0YXNldCRhdmVyYWdlX2dyYWRlcyA+PSA4NV0gPC0gJytCJw0KRGF0YXNldCRhdmVyYWdlX2dyYWRlcyBbODUgPkRhdGFzZXQkYXZlcmFnZV9ncmFkZXMgJiBEYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzID49IDgwXSA8LSAnQicNCkRhdGFzZXQkYXZlcmFnZV9ncmFkZXMgWzgwID5EYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzICYgRGF0YXNldCRhdmVyYWdlX2dyYWRlcyA+PSA3NV0gPC0gJytDJw0KRGF0YXNldCRhdmVyYWdlX2dyYWRlcyBbNzUgPkRhdGFzZXQkYXZlcmFnZV9ncmFkZXMgJiBEYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzID49IDcwXSA8LSAnQycNCkRhdGFzZXQkYXZlcmFnZV9ncmFkZXMgWzcwID5EYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzICYgRGF0YXNldCRhdmVyYWdlX2dyYWRlcyA+PSA2NV0gPC0gJytEJw0KRGF0YXNldCRhdmVyYWdlX2dyYWRlcyBbNjUgPkRhdGFzZXQkYXZlcmFnZV9ncmFkZXMgJiBEYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzID49IDYwXSA8LSAnRCcNCkRhdGFzZXQkYXZlcmFnZV9ncmFkZXMgWzYwID5EYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzICYgRGF0YXNldCRhdmVyYWdlX2dyYWRlcyA+PSAwXSA8LSAnRicNCkRhdGFzZXQkYXZlcmFnZV9ncmFkZXMgPC0gYXMuY2hhcmFjdGVyKERhdGFzZXQkYXZlcmFnZV9ncmFkZXMgKQ0KcHJpbnQoRGF0YXNldCkNCmBgYA0KDQpXZSB0cmFuc2Zvcm1lZCB0aGUgcGFyZW50X2FnZSBhdHRyaWJ1dGUgaW50byBpbnRlcnZhbHMgYnkgZGl2aWRpbmcgdGhlIHZhbHVlcyB0byBiZSBmYWxsIG9uIG9uZSBvZiB0d28gcG9zc2libGUgaW50ZXJ2YWwgbGFiZWxzIHdpdGggZXF1YWwgd2lkdGggd2hpY2ggaXMoNDAsNTBdLCg1MCw2MF0gYnkgZGlzY3JldGl6YXRpb24gdGhlIHZhbHVlcyB3ZWxsIGJlIHNpbXBsZXIgdG8gY2xhc3NpZnkgb3IgcGVyZm9ybSBvdGhlciBtZXRob2RzIHRoYXQgY2FuIGhlbHAgdXMgbGF0ZXIgaW4gb3VyIG1vZGVsLg0KDQphbmQgdG8gYmV0dGVyIHV0aWxpemUgYW5kIGludGVycHJldCB0aGUgZ3JhZGVzIGF0dHJpYnV0ZXMgZm9yIGVhY2ggc3R1ZGVudCwgd2UgaGF2ZSBjb252ZXJ0ZWQgdGhlIG51bWVyaWMgZ3JhZGVzIGludG8gbGV0dGVyIGdyYWRlcyAoQSssIEEsIEIrLCBCLCBDKywgQywgRCssIEQsIEYpLiBUaGlzIHRyYW5zZm9ybWF0aW9uIHdhcyB1bmRlcnRha2VuIHRvIGZvY3VzIG9uIHRoZSBnZW5lcmFsIGxldHRlciBncmFkZSByZXByZXNlbnRhdGlvbiByYXRoZXIgdGhhbiB0aGUgcHJlY2lzZSBudW1lcmljYWwgdmFsdWVzLg0KDQojIyBFbmNvZGluZw0KDQpgYGB7cn0NCkRhdGFzZXQkcGFyZW50X3dhc19pbl9jb2xsZWdlW0RhdGFzZXQkcGFyZW50X3dhc19pbl9jb2xsZWdlPT0iVFJVRSJdPC0xDQpEYXRhc2V0JHBhcmVudF93YXNfaW5fY29sbGVnZVtEYXRhc2V0JHBhcmVudF93YXNfaW5fY29sbGVnZT09IlRydWUiXTwtMQ0KRGF0YXNldCRwYXJlbnRfd2FzX2luX2NvbGxlZ2VbRGF0YXNldCRwYXJlbnRfd2FzX2luX2NvbGxlZ2U9PSJGQUxTRSJdPC0wDQpEYXRhc2V0JHBhcmVudF93YXNfaW5fY29sbGVnZVtEYXRhc2V0JHBhcmVudF93YXNfaW5fY29sbGVnZT09IkZhbHNlIl08LTANCg0KRGF0YXNldCR3aWxsX2dvX3RvX2NvbGxlZ2VbRGF0YXNldCR3aWxsX2dvX3RvX2NvbGxlZ2U9PSJUUlVFIl08LTANCkRhdGFzZXQkd2lsbF9nb190b19jb2xsZWdlW0RhdGFzZXQkd2lsbF9nb190b19jb2xsZWdlPT0iVHJ1ZSJdPC0wDQpEYXRhc2V0JHdpbGxfZ29fdG9fY29sbGVnZVtEYXRhc2V0JHdpbGxfZ29fdG9fY29sbGVnZT09IkZBTFNFIl08LTENCkRhdGFzZXQkd2lsbF9nb190b19jb2xsZWdlW0RhdGFzZXQkd2lsbF9nb190b19jb2xsZWdlPT0iRmFsc2UiXTwtMQ0KDQpEYXRhc2V0JGdlbmRlcltEYXRhc2V0JGdlbmRlcj09IkZlbWFsZSJdPC0xDQpEYXRhc2V0JGdlbmRlcltEYXRhc2V0JGdlbmRlcj09Ik1hbGUiXTwtMA0KDQpEYXRhc2V0JHNjaG9vbF9hY2NyZWRpdGF0aW9uW0RhdGFzZXQkc2Nob29sX2FjY3JlZGl0YXRpb249PSJBIl08LTENCkRhdGFzZXQkc2Nob29sX2FjY3JlZGl0YXRpb25bRGF0YXNldCRzY2hvb2xfYWNjcmVkaXRhdGlvbj09IkIiXTwtMA0KDQpEYXRhc2V0JGludGVyZXN0W0RhdGFzZXQkaW50ZXJlc3Q9PSJWZXJ5IEludGVyZXN0ZWQiXTwtNA0KRGF0YXNldCRpbnRlcmVzdFtEYXRhc2V0JGludGVyZXN0PT0iSW50ZXJlc3RlZCJdPC0zDQpEYXRhc2V0JGludGVyZXN0W0RhdGFzZXQkaW50ZXJlc3Q9PSJMZXNzIEludGVyZXN0ZWQiXTwtMg0KRGF0YXNldCRpbnRlcmVzdFtEYXRhc2V0JGludGVyZXN0PT0iTm90IEludGVyZXN0ZWQiXTwtMQ0KRGF0YXNldCRpbnRlcmVzdFtEYXRhc2V0JGludGVyZXN0PT0iVW5jZXJ0YWluIl08LTANCg0KDQpEYXRhc2V0JHR5cGVfc2Nob29sW0RhdGFzZXQkdHlwZV9zY2hvb2w9PSJBY2FkZW1pYyJdPC0xIA0KRGF0YXNldCR0eXBlX3NjaG9vbFtEYXRhc2V0JHR5cGVfc2Nob29sPT0iVm9jYXRpb25hbCJdPC0wDQoNCkRhdGFzZXQkcmVzaWRlbmNlW0RhdGFzZXQkcmVzaWRlbmNlPT0iVXJiYW4iXTwtMQ0KRGF0YXNldCRyZXNpZGVuY2VbRGF0YXNldCRyZXNpZGVuY2U9PSJSdXJhbCJdPC0wDQpwcmludChEYXRhc2V0KQ0KYGBgDQoNClNpbmNlIGVuY29kaW5nIGlzIGFuIGltcG9ydGFudCBzdGVwIGluIGRhdGEgcHJlcHJvY2Vzc2luZyB0aGF0IGVuYWJsZXMgdGhlIHVzZSBvZiBjYXRlZ29yaWNhbCBkYXRhIGluIHZhcmlvdXMgZGF0YSBhbmFseXNpcyBhbmQgbWFjaGluZSBsZWFybmluZyB0YXNrcywgd2UgZW5jb2RlZCBhdHRyaWJ1dGVzIGxpa2UgdGhlICdwYXJlbnQgd2FzIGluIGNvbGxlZ2UnIGF0dHJpYnV0ZSBmcm9tIChUcnVlLCBGYWxzZSkgdG8gKDEsIDApLCBhbmQgJ3dpbGwgZ28gdG8gY29sbGVnZScgZnJvbSAoVHJ1ZSwgRmFsc2UpIHRvICgwLCAxKS4gVGhpcyBlbmNvZGluZyBpcyBjYXJyaWVkIG91dCBhcyB3ZSBhaW0gdG8gcHJlZGljdCB0aGUgaW5mbHVlbmNpbmcgZmFjdG9ycy4gQWRkaXRpb25hbGx5LCB3ZSBlbmNvZGVkIHRoZSAnZ2VuZGVyJyBhdHRyaWJ1dGUgZnJvbSAoRmVtYWxlLCBNYWxlKSB0byAoMSwgMCksICdzY2hvb2wgYWNjcmVkaXRhdGlvbicgZnJvbSAoQSwgQikgdG8gKDEsIDApLCAndHlwZV9zY2hvb2wnIGZyb20gKEFjYWRlbWljLCBWb2NhdGlvbmFsKSB0byAoMSwgMCksICdyZXNpZGVuY2UnIGZyb20gKFVyYmFuLCBSdXJhbCkgdG8gKDEsIDApLCBhbmQgJ2ludGVyZXN0JyBmcm9tIChWZXJ5IGludGVyZXN0ZWQgLEludGVyZXN0ZWQgLCBMZXNzIEludGVyZXN0ZWQgLCBOb3QgSW50ZXJlc3RlZCAsVW5jZXJ0YWluICkgdG8gKDQsMywyLCAxLCAwKSByZXNwZWN0aXZlbHkuIEVuY29kaW5nIHNlcnZlcyB0byBzaW1wbGlmeSB0aGUgZGF0YSwgcmVkdWNlIGNvbXBsZXhpdHksIGFuZCBlbmhhbmNlIGl0cyBzdWl0YWJpbGl0eSBmb3IgbW9kZWxpbmcgcHVycG9zZXMuDQoNCiMjIENvcnJlbGF0aW9uIGFuYWx5c2lzIENoaSBzcXVhcmUgdGVzdCBmb3Igbm9taW5hbCBhdHRyaWJ1dGU6DQoNCmBgYHtyfQ0KDQojMQ0KQz1jaGlzcS50ZXN0KERhdGFzZXQkdHlwZV9zY2hvb2wgLCBEYXRhc2V0JHdpbGxfZ29fdG9fY29sbGVnZSkNCnByaW50KEMpDQojMg0KDQpDPWNoaXNxLnRlc3QoRGF0YXNldCRzY2hvb2xfYWNjcmVkaXRhdGlvbiAsIERhdGFzZXQkd2lsbF9nb190b19jb2xsZWdlKQ0KcHJpbnQoQykNCiMzDQpDPWNoaXNxLnRlc3QoRGF0YXNldCRnZW5kZXIgLCBEYXRhc2V0JHdpbGxfZ29fdG9fY29sbGVnZSkNCnByaW50KEMpDQojNA0KQz1jaGlzcS50ZXN0KERhdGFzZXQkaW50ZXJlc3QgLCBEYXRhc2V0JHdpbGxfZ29fdG9fY29sbGVnZSkNCnByaW50KEMpDQoNCiM1DQpDPWNoaXNxLnRlc3QoRGF0YXNldCRyZXNpZGVuY2UgLCBEYXRhc2V0JHdpbGxfZ29fdG9fY29sbGVnZSkNCnByaW50KEMpDQoNCiM2DQpDPWNoaXNxLnRlc3QoRGF0YXNldCRhdmVyYWdlX2dyYWRlcyAsIERhdGFzZXQkd2lsbF9nb190b19jb2xsZWdlKQ0KcHJpbnQoQykNCg0KIzcNCkM9Y2hpc3EudGVzdChEYXRhc2V0JHBhcmVudF93YXNfaW5fY29sbGVnZSAsIERhdGFzZXQkd2lsbF9nb190b19jb2xsZWdlKQ0KcHJpbnQoQykNCmBgYA0KDQpBbGwgdGhlIGF0dHJpYnV0ZXMgaGF2ZSBYLXNxdWFyZSBncmVhdGVyIHRoYW4gdGhlIHAtdmFsdWUgd2hpY2ggaW5kaWNhdGUgYSBzb21lIGFzc29jaWF0aW9uIHdpdGggdGhlIGNsYXNzIGxhYmVsOyB0aGVyZWZvcmUgd2UgcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMNCg0Kd2Ugbm90aWNlZCBmb3IgJ2ludGVyZXN0JyBhbmQgJ2F2ZXJhZ2UgZ3JhZGUnIHRoZSBhbmFseXNpcyBzaG93cyB0aGF0IFgtc3F1YXJlIGlzIG11Y2ggbGFyZ2VyIHRoYW4gcC12YWx1ZSBpbmRpY2F0ZSB0aGUgc2lnbmlmaWNhbnQgYXNzb2NpYXRpb24gb2YgdGhlIHR3byBhdHRyaWJ1dGVzIHdpdGggdGhlIGRlY2lzaW9uIG9mIHRoZSBzdHVkZW50IHRvIGdvIHRvIHRoZSBjb2xsYWdlIG9yIG5vdA0KDQojIyBDb3JyZWxhdGlvbiBjb2VmZmljaWVudCBhbmFseXNpcyBmb3IgbnVtZXJpYyBhdHRyaWJ1dGU6DQoNCmBgYHtyfQ0KDQpiaXNlcmlhbC5jb3IoRGF0YXNldCRwYXJlbnRfc2FsYXJ5LERhdGFzZXQkd2lsbF9nb190b19jb2xsZWdlLCBjKCJhbGwub2JzIiwgImNvbXBsZXRlLm9icyIpLCBsZXZlbCA9IDEpDQpiaXNlcmlhbC5jb3IoRGF0YXNldCRob3VzZV9hcmVhLERhdGFzZXQkd2lsbF9nb190b19jb2xsZWdlLCBjKCJhbGwub2JzIiwgImNvbXBsZXRlLm9icyIpLCBsZXZlbCA9IDEpDQpiaXNlcmlhbC5jb3IoRGF0YXNldCRwYXJlbnRfYWdlLERhdGFzZXQkd2lsbF9nb190b19jb2xsZWdlLGMoImFsbC5vYnMiLCAiY29tcGxldGUub2JzIiksIGxldmVsID0gMSkNCiANCiANCg0KYGBgDQoNCnRoZSBhbmFseXNpcyBzaG93cyBtb2RlcmF0ZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBmb3IgcGFyZW50IHNhbGFyeSBhbmQgaG91c2UgYXJlYSB3aXRoIHRoZSBjbGFzcyBsYWJlbCB3aGljaCBpbmRpY2F0ZSB0aGF0IHRoZXkgYXJlIHJlbGV2YW50IGZhY3RvcnMgbWVhbmluZyB0aGF0IHRoZSBoaWdoZXIgdGhlIHBhcmVudCBzYWxhcnkgYW5kIHRoZSBsYXJnZXIgaG91c2UgYXJlYSB0aGUgaGlnaGVyIHByb2JhYmlsaXR5IGZvciBhIHN0dWRlbnQgdG8gZW5yb2xsIGluIGEgY29sbGFnZQ0KDQp3aGVyZSBpcyB0aGUgb24gb3RoZXIgaGFuZCwgdGhlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IGZvciB0aGUgcGFyZW50IGFnZSBpcyB2ZXJ5IHNtYWxsIHdoaWNoIGluZGljYXRlIHRoYXQgdGhlIHBhcmVudCBhZ2UgaGFzIGxpdHRsZSBpbXBhY3QgdG8gdGhlIHByb2JhYmlsaXR5IGZvciBzdHVkZW50IHRvIGVucm9sbCBpbiBhIGNvbGxhZ2UNCg0KIyMgRmVhdHVyZSBzZWxlY3Rpb246DQoNCnVsdGltYXRlbHkgYmFzZWQgb24gdGhlIGFuYWx5c2lzIG9mIHRoZSBjb3JyZWxhdGlvbiB0aGF0IHdlIGNvbmR1Y3RlZCBvbiB0aGUgcmVsYXRpb25zaGlwIG9mIHRoZSBkYXRhc2V0IGF0dHJpYnV0ZXMgd2l0aCB0aGUgY2xhc3MgbGFiZWwsIGFuZCB0aGUgdW5kZXJzdGFuZGluZyBvZiB0aGUgZGF0YSBhbmQgdGhlIGNvbnRleHQgb2YgZWFjaCBhdHRyaWJ1dGUgYW5kIHBvdGVudGlhbCByZWxldmFuY2UgdG8gdGhlIGNsYXNzIGxhYmVsIHdlIGRlY2lkZWQgdG8gbm90IGRlbGV0ZSBhbnkgb2YgdGhlIGF0dHJpYnV0ZQ0KDQojIyBDbGFzc2lmaWNhdGlvbjoNCg0KVGhlIGF0dHJpYnV0ZSBzZWxlY3Rpb24gbWVhc3VyZSBpcyBhIGhldXJpc3RpYyB0ZWNobmlxdWUgZW1wbG95ZWQgdG8gc2VsZWN0IHRoZSBtb3N0IG9wdGltYWwgY3JpdGVyaWEgZm9yIGRpdmlkaW5nIGEgY29sbGVjdGlvbiBvZiBkYXRhIHR1cGxlcyBpbnRvIHBhcnRpdGlvbnMgdGhhdCBhcmUgYXMgcHVyZSBhcyBwb3NzaWJsZS4gU2V2ZXJhbCBhdHRyaWJ1dGUgc2VsZWN0aW9uIG1lYXN1cmVzIGFyZSBjb21tb25seSB1dGlsaXplZCwgc3VjaCBhcyBJbmZvcm1hdGlvbiBHYWluIChJRDMpLCBHYWluIFJhdGlvIChDNC41KSwgYW5kIEdpbmkgSW5kZXggKENBUlQpLiB3ZSB3aWxsIGNvbnN0cmFpbnRzIGZvciBFYWNoIG9mIHRoZXNlIG1lYXN1cmVzIGRpZmZlcmVudCBwYXJ0aXRpb25zIGJ5IGRpdmlkaW5nIHRoZW0gaW4gcmF0aW9zIG9mIDUwJS01MCUsIDcwJS0zMCUsIGFuZCA4MCUtMjAlLg0KDQojIyMgZmFjdG9yIHRoZSBkYXRhDQoNCmBgYHtyfQ0KDQoNCmRhdGE8LVByZXByb2Nlc3NlZF9kYXRhc2V0DQpkYXRhJHdpbGxfZ29fdG9fY29sbGVnZSA8LSBhcy5mYWN0b3IoZGF0YSR3aWxsX2dvX3RvX2NvbGxlZ2UpDQpkYXRhJHJlc2lkZW5jZSA8LSBhcy5mYWN0b3IoZGF0YSRyZXNpZGVuY2UpDQpkYXRhJGdlbmRlciA8LSBhcy5mYWN0b3IoZGF0YSRnZW5kZXIpDQpkYXRhJHBhcmVudF93YXNfaW5fY29sbGVnZSA8LSBhcy5mYWN0b3IoZGF0YSRwYXJlbnRfd2FzX2luX2NvbGxlZ2UpDQpkYXRhJGludGVyZXN0IDwtIGFzLmZhY3RvcihkYXRhJGludGVyZXN0KQ0KZGF0YSR0eXBlX3NjaG9vbCA8LSBhcy5mYWN0b3IoZGF0YSR0eXBlX3NjaG9vbCkNCmRhdGEkc2Nob29sX2FjY3JlZGl0YXRpb24gPC0gYXMuZmFjdG9yKGRhdGEkc2Nob29sX2FjY3JlZGl0YXRpb24pDQpkYXRhJGF2ZXJhZ2VfZ3JhZGVzIDwtIGFzLmZhY3RvcihkYXRhJGF2ZXJhZ2VfZ3JhZGVzKQ0KDQpgYGANCg0KIyMjIGJhbGFuY2VkIG9yIGltYmFsYW5jZWQNCg0KYGBge3J9DQoNCmxpYnJhcnkodGlkeXZlcnNlKSANCmxpYnJhcnkoY2FyZXQpDQpkYXRhJHdpbGxfZ29fdG9fY29sbGVnZTwtIGFzLm51bWVyaWMoZGF0YSR3aWxsX2dvX3RvX2NvbGxlZ2UpDQpoaXN0KGRhdGEkd2lsbF9nb190b19jb2xsZWdlLGNvbD0iY29yYWwiKQ0KcHJvcC50YWJsZSh0YWJsZShkYXRhJHdpbGxfZ29fdG9fY29sbGVnZSkpDQpgYGANCg0Kd2Ugd2FudCB0byBjb25maXJtIHRoYXQgdGhlIGRpc3RyaWJ1dGlvbiBiZXR3ZWVuIHRoZSB0d28gbGFiZWwgZGF0YSBpcyBub3QgdG9vIG11Y2ggZGlmZmVyZW50LiBCZWNhdXNlIGltYmFsYW5jZWQgZGF0YXNldHMgY2FuIGxlYWQgdG8gaW1iYWxhbmNlZCBhY2N1cmFjeS4NCg0KRm9ydHVuYXRlbHkgLG91ciBkYXRhIGlzIGJhbGFuY2VkDQoNCnwgKio3MCUgdHJhaW5pbmcsIDMwJSB0ZXN0aW5nKiogfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKQ0KaW5kPXNhbXBsZSgyLCBucm93KGRhdGEpLCByZXBsYWNlPVRSVUUsIHByb2I9YygwLjcwICwgMC4zMCkpDQp0cmFpbl9kYXRhPWRhdGFbaW5kPT0xLF0NCnRlc3RfZGF0YT1kYXRhW2luZD09MixdDQpkaW0odHJhaW5fZGF0YSkNCmRpbSh0ZXN0X2RhdGEpDQpgYGANCg0KV2UgcGFydGl0aW9uIHRoZSBkYXRhIHRoZSBkYXRhIGludG8gKDcwJSB0cmFpbmluZywgMzAlIHRlc3RpbmcpLiBUaGlzIHJlc3VsdCBpbiA3MDUgb2JqZWN0IGluIHRoZSB0cmFpbmluZyBzZXQgYW5kIDI5NSBvYmplY3QgaW4gdGhlIHRlc3Rpbmcgc2V0Lg0KDQojIyMjICoqaW5mb3JtYXRpb24gZ2FpbioqDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KHBhcnR5KQ0KbXlGb3JtdWxhPC0gd2lsbF9nb190b19jb2xsZWdlfiArIHR5cGVfc2Nob29sK3NjaG9vbF9hY2NyZWRpdGF0aW9uK2dlbmRlcitpbnRlcmVzdCtyZXNpZGVuY2UrcGFyZW50X2FnZStwYXJlbnRfc2FsYXJ5K2hvdXNlX2FyZWErYXZlcmFnZV9ncmFkZXMrcGFyZW50X3dhc19pbl9jb2xsZWdlDQoNCmRhdGFzZXRfY3RyZWU8LWN0cmVlKG15Rm9ybXVsYSwgZGF0YT10cmFpbl9kYXRhKQ0KdGFibGUocHJlZGljdChkYXRhc2V0X2N0cmVlKSwgdHJhaW5fZGF0YSR3aWxsX2dvX3RvX2NvbGxlZ2UpDQpwbG90KGRhdGFzZXRfY3RyZWUsdHlwZT0ic2ltcGxlIikNCnRlc3RQcmVkIDwtIHByZWRpY3QoZGF0YXNldF9jdHJlZSwgbmV3ZGF0YSA9IHRlc3RfZGF0YSkNCnJlc3VsdDwtdGFibGUodGVzdFByZWQsIHRlc3RfZGF0YSR3aWxsX2dvX3RvX2NvbGxlZ2UgKQ0KbGlicmFyeShjYXJldCkNCmNvX3Jlc3VsdCA8LSBjb25mdXNpb25NYXRyaXgocmVzdWx0LHBvc2l0aXZlPSIxIikNCnByaW50KGNvX3Jlc3VsdCkNCmBgYA0KDQohW10oaW1hZ2VzL1doYXRzQXBwJTIwSW1hZ2UlMjAyMDIzLTExLTMwJTIwYXQlMjAxOS4zNi4yOV8wZTg1ZTQxOC5qcGcpDQoNCnRoaXMgbWF0cml4IGJlbG93IHNob3cgdGhlIGV2YWx1YXRpb24gbW9kZWwgb24gdGVzdGluZyBkYXRhDQoNCnwgRXZhbHVhdGlvbiBtZXRob2QgfCBNZXRob2QgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS18DQp8IEFjY3VyYWN5ICAgICAgICAgIHwgODIuMzclIHwNCnwgRXJyb3IgcmF0ZSAgICAgICAgfCAxNy42MyUgfA0KfCBTZW5zaXRpdml0eSAgICAgICB8IDc1Ljg5JSB8DQp8IFNwZWNpZmljaXR5ICAgICAgIHwgODguMzElIHwNCnwgUHJlY2lzaW9uICAgICAgICAgfCA4NS42MCUgfA0KDQpJbiB0aGlzIGRlY2lzaW9uIHRyZWUgbW9kZWwsIHRoZSBhdmVyYWdlIGdyYWRlIHdpdGggdGhlIGhpZ2hlc3QgaW5mb3JtYXRpb24gZ2FpbiBpcyBzZWxlY3RlZCBhcyB0aGUgcm9vdCBhdHRyaWJ1dGUuIFRoaXMgbWVhbnMgdGhhdCB0aGUgYXZlcmFnZSBncmFkZSBpcyBjb25zaWRlcmVkIHRoZSBtb3N0IGluZm9ybWF0aXZlIGZlYXR1cmUgZm9yIGNsYXNzaWZ5aW5nIHRoZSBkYXRhLg0KDQpUbyBlbmhhbmNlIHRoZSBtb2RlbCdzIHByZWRpY3RpdmUgY2FwYWJpbGl0eSwgYWRkaXRpb25hbCBzcGxpdHRpbmcgY3JpdGVyaWEgYXJlIGluY29ycG9yYXRlZCBiYXNlZCBvbiB0aGVpciBpbmZvcm1hdGlvbiBnYWluIHZhbHVlcy4gVGhlc2UgY3JpdGVyaWEgaW5jbHVkZSB0eXBlX3NjaG9vbCwgaW50ZXJlc3QsIHJlc2lkZW5jZSwgcGFyZW50X3NhbGFyeSwgYW5kIGhvdXNlX2FyZWEuIEJ5IGNvbnNpZGVyaW5nIHRoZXNlIGF0dHJpYnV0ZXMsIHRoZSBtb2RlbCBhaW1zIHRvIGNhcHR1cmUgbW9yZSByZWxldmFudCBpbmZvcm1hdGlvbiBmcm9tIHRoZSBkYXRhIGFuZCBpbXByb3ZlIGl0cyBjbGFzc2lmaWNhdGlvbiBhY2N1cmFjeS4NCg0KVGhlIG1vZGVsIHRoZW4gdXRpbGl6ZXMgdGhlIGhvdXNlIGFyZWEgYW5kIGF2ZXJhZ2UgZ3JhZGVzIGFzIHNwbGl0dGluZyBjcml0ZXJpYSB0byBzZXBhcmF0ZSB0aGUgZGF0YSBpbnRvIGRpZmZlcmVudCBicmFuY2hlcyBvZiB0aGUgdHJlZSBpbiBtdWx0aXBsZXMgbGV2ZWxzDQoNCkhvd2V2ZXIsIGl0J3Mgd29ydGggbm90aW5nIHRoYXQgdGhlIGRlY2lzaW9uIHRyZWUgaW4gdGhpcyBjYXNlIGlzIGNvbnNpZGVyZWQgdG8gaGF2ZSBhIGxvdyBsZXZlbC4gVGhpcyBpbXBsaWVzIHRoYXQgdGhlIHRyZWUgaXMgcmVsYXRpdmVseSBzaGFsbG93IGFuZCBtYXkgbm90IGJlIGFibGUgdG8gY2FwdHVyZSBpbnRyaWNhdGUgcGF0dGVybnMgb3IgcmVsYXRpb25zaGlwcyBwcmVzZW50IGluIHRoZSBkYXRhLiBGb3IgZXhhbXBsZSBnZW5kZXIsIHBhcmVudF93YXNfaW5fY29sbGVnZSwgcGFyZW50IGFnZSwgYW5kIHNjaG9vbCBhY2NyZWRpdGF0aW9uIGFyZSBub3QgaW5jbHVkZWQgaW4gdGhlIHRyZWUgZHVlIHRvIGxpbWl0ZWQgYXZhaWxhYmlsaXR5IG9mIHRoZSBkYXRhIG9yIHRoZWlyIGxvd2VyIGluZm9ybWF0aW9uIGdhaW4gdmFsdWVzLg0KDQojIyMjICoqR2FpbiByYXRpbyoqDQoNCmBgYHtyfQ0KbGlicmFyeShSV2VrYSkNCkM0NUZpdCA8LSBKNDgoIHdpbGxfZ29fdG9fY29sbGVnZX4uLGRhdGE9dHJhaW5fZGF0YSkNCnRhYmxlKHRyYWluX2RhdGEkd2lsbF9nb190b19jb2xsZWdlLCBwcmVkaWN0KEM0NUZpdCkpDQpwbG90KEM0NUZpdCx0eXBlID0gInNpbXBsZSIpDQp0ZXN0UHJlZCA8LSBwcmVkaWN0KEM0NUZpdCwgbmV3ZGF0YSA9IHRlc3RfZGF0YSkNCnJlc3VsdHMgPC0gY29uZnVzaW9uTWF0cml4KHRlc3RQcmVkLCB0ZXN0X2RhdGEkd2lsbF9nb190b19jb2xsZWdlLCBwb3NpdGl2ZSA9ICIxIikNCnByaW50KHJlc3VsdHMpDQpgYGANCg0KIVtdKGltYWdlcy9XaGF0c0FwcCUyMEltYWdlJTIwMjAyMy0xMS0zMCUyMGF0JTIwMTkuNTMuMzBfNGVhY2JjODYuanBnKQ0KDQp0aGlzIG1hdHJpeCBiZWxvdyBzaG93IHRoZSBldmFsdWF0aW9uIG1vZGVsIG9uIHRlc3RpbmcgZGF0YQ0KDQp8IEV2YWx1YXRpb24gbWV0aG9kIHwgTWV0aG9kIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tfA0KfCBBY2N1cmFjeSAgICAgICAgICB8IDgwLjM0JSB8DQp8IEVycm9yIHJhdGUgICAgICAgIHwgMTkuNjYlIHwNCnwgU2Vuc2l0aXZpdHkgICAgICAgfCA3Ny4zMCUgfA0KfCBTcGVjaWZpY2l0eSAgICAgICB8IDgzLjEyJSB8DQp8IFByZWNpc2lvbiAgICAgICAgIHwgODAuNzQlIHwNCg0KSW4gY29udHJhc3QgdG8gdGhlIHByZXZpb3VzIGRlY2lzaW9uIHRyZWUsIHRoZSBjdXJyZW50IHRyZWUgaXMgbW9yZSBjb21wbGV4IGFuZCBpbmNsdWRlcyBhbGwgdGhlIGF0dHJpYnV0ZXMgZXhjZXB0IGZvciBnZW5kZXIuIFRoaXMgbWVhbnMgdGhhdCB0aGUgZGVjaXNpb24gdHJlZSB0YWtlcyBpbnRvIGFjY291bnQgYSB3aWRlciByYW5nZSBvZiBmYWN0b3JzIHRvIG1ha2UgaXRzIGNsYXNzaWZpY2F0aW9ucy4gVGhlIGF0dHJpYnV0ZSB3aXRoIHRoZSBoaWdoZXN0IEdhaW4gcmF0aW8sIHdoaWNoIG1lYXN1cmVzIHRoZSBlZmZlY3RpdmVuZXNzIG9mIGEgc3BsaXQsIGlzIGNob3NlbiBhcyB0aGUgcm9vdCBhdHRyaWJ1dGUuIEluIHRoaXMgY2FzZSwgUGFyZW50IHNhbGFyeSBpcyBkZXRlcm1pbmVkIHRvIGhhdmUgdGhlIGhpZ2hlc3QgR2FpbiByYXRpbyBhbmQgaXMgc2VsZWN0ZWQgYXMgdGhlIHJvb3QgYXR0cmlidXRlLg0KDQpUaGUgbW9kZWwgdGhlbiByZXBlYXRlZGx5IHVzZXMgYXZlcmFnZSBncmFkZXMgYXMgdGhlIHNwbGl0dGluZyBjcml0ZXJpb24gYXQgZGlmZmVyZW50IGxldmVscyBvZiB0aGUgdHJlZS4gVGhpcyBpbmRpY2F0ZXMgdGhhdCB0aGUgYXZlcmFnZSBncmFkZXMgYXJlIGNvbnNpZGVyZWQgY3J1Y2lhbCBmb3IgY2xhc3NpZnlpbmcgdGhlIGRhdGEgYW5kIGZ1cnRoZXIgc3ViZGl2aWRpbmcgdGhlIGJyYW5jaGVzLg0KDQojIyMjICoqR2luaSBpbmRleCoqDQoNCmBgYHtyfQ0KbGlicmFyeShycGFydCkNCmxpYnJhcnkocnBhcnQucGxvdCkNCmNhcnRfZml0PXJwYXJ0KHdpbGxfZ29fdG9fY29sbGVnZX4uLCBkYXRhPXRyYWluX2RhdGEsIG1ldGhvZD0iY2xhc3MiLGNwPTAuMDA4KQ0KDQoNCnJwYXJ0LnBsb3QoY2FydF9maXQpDQoNCnRlc3RQcmVkPC0gcHJlZGljdChjYXJ0X2ZpdCxuZXdkYXRhPXRlc3RfZGF0YSx0eXBlPSJjbGFzcyIpDQpyZXN1bHRzPC0gY29uZnVzaW9uTWF0cml4KHRlc3RQcmVkLHRlc3RfZGF0YSR3aWxsX2dvX3RvX2NvbGxlZ2UscG9zaXRpdmU9IjEiKQ0KcHJpbnQocmVzdWx0cykNCg0KYGBgDQoNCnRoaXMgbWF0cml4IGJlbG93IHNob3cgdGhlIGV2YWx1YXRpb24gbW9kZWwgb24gdGVzdGluZyBkYXRhDQoNCnwgRXZhbHVhdGlvbiBtZXRob2QgfCBNZXRob2QgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS18DQp8IEFjY3VyYWN5ICAgICAgICAgIHwgNzkuNjYlIHwNCnwgRXJyb3IgcmF0ZSAgICAgICAgfCAyMC4zNCUgfA0KfCBTZW5zaXRpdml0eSAgICAgICB8IDc4LjAxJSB8DQp8IFNwZWNpZmljaXR5ICAgICAgIHwgODEuMTclIHwNCnwgUHJlY2lzaW9uICAgICAgICAgfCA3OS4xNCUgfA0KDQpJbiB0aGlzIHBhcnRpY3VsYXIgcGFydGl0aW9uIG9mIHRoZSBkZWNpc2lvbiB0cmVlLCBQYXJlbnQgc2FsYXJ5IHdhcyBvbmNlIGFnYWluIGNob3NlbiBhcyB0aGUgcm9vdCBhdHRyaWJ1dGUgZHVlIHRvIGl0cyBoaWdoIEdhaW4gaW5kZXguDQoNCkdlbmRlciBhbmQgcmVzaWRlbmNlLCBvbiB0aGUgb3RoZXIgaGFuZCwgd2VyZSBub3QgaW5jbHVkZWQgaW4gdGhpcyBwYXJ0aXRpb24gb2YgdGhlIHRyZWUuIFdpdGhvdXQgc3VmZmljaWVudCBkYXRhIG9yIGEgc2lnbmlmaWNhbnQgaW5mb3JtYXRpb24gZ2FpbiBhc3NvY2lhdGVkIHdpdGggdGhlc2UgYXR0cmlidXRlcywgdGhlIG1vZGVsIG1heSBub3QgaGF2ZSBlbm91Z2ggZXZpZGVuY2UgdG8gdXRpbGl6ZSB0aGVtIGZvciBhY2N1cmF0ZSBjbGFzc2lmaWNhdGlvbi4NCg0KSXQncyB3b3J0aCBub3RpbmcgdGhhdCBnZW5kZXIgd2FzIG5vdCBpbmNsdWRlZCBpbiB0aGlzIHBhcnRpdGlvbiwgYW5kIGl0IGlzIGltcG9ydGFudCB0byBoaWdobGlnaHQgdGhhdCB0aGUgYWNjdXJhY3kgb2YgdGhpcyBzcGVjaWZpYyBtb2RlbCB3YXMgcmVsYXRpdmVseSBsb3dlciwgYXQgNzkuNjYlLg0KDQp8IDgwJSB0cmFpbmluZywgMjAlIHRlc3RpbmcgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQppbmQ9c2FtcGxlKDIsIG5yb3coZGF0YSksIHJlcGxhY2U9VFJVRSwgcHJvYj1jKDAuODAgLCAwLjIwKSkNCnRyYWluX2RhdGE9ZGF0YVtpbmQ9PTEsXQ0KdGVzdF9kYXRhPWRhdGFbaW5kPT0yLF0NCmRpbSh0cmFpbl9kYXRhKQ0KZGltKHRlc3RfZGF0YSkNCmBgYA0KDQpXZSBwYXJ0aXRpb24gdGhlIGRhdGEgdGhlIGRhdGEgaW50byAoODAlIHRyYWluaW5nLCAyMCUgdGVzdGluZykuIFRoaXMgcmVzdWx0IGluIDgwMiBvYmplY3QgaW4gdGhlIHRyYWluaW5nIHNldCBhbmQgMTk4IG9iamVjdCBpbiB0aGUgdGVzdGluZyBzZXQuDQoNCiMjIyMgKippbmZvcm1hdGlvbiBnYWluKioNCg0KYGBge3J9DQpsaWJyYXJ5KHBhcnR5KQ0KbXlGb3JtdWxhPC0gd2lsbF9nb190b19jb2xsZWdlfiArIHR5cGVfc2Nob29sK3NjaG9vbF9hY2NyZWRpdGF0aW9uK2dlbmRlcitpbnRlcmVzdCtyZXNpZGVuY2UrcGFyZW50X2FnZStwYXJlbnRfc2FsYXJ5K2hvdXNlX2FyZWErYXZlcmFnZV9ncmFkZXMrcGFyZW50X3dhc19pbl9jb2xsZWdlDQoNCmRhdGFzZXRfY3RyZWU8LWN0cmVlKG15Rm9ybXVsYSwgZGF0YT10cmFpbl9kYXRhKQ0KdGFibGUocHJlZGljdChkYXRhc2V0X2N0cmVlKSwgdHJhaW5fZGF0YSR3aWxsX2dvX3RvX2NvbGxlZ2UpDQpwbG90KGRhdGFzZXRfY3RyZWUsdHlwZT0ic2ltcGxlIikNCnRlc3RQcmVkIDwtIHByZWRpY3QoZGF0YXNldF9jdHJlZSwgbmV3ZGF0YSA9IHRlc3RfZGF0YSkNCnJlc3VsdDwtdGFibGUodGVzdFByZWQsIHRlc3RfZGF0YSR3aWxsX2dvX3RvX2NvbGxlZ2UgKQ0KbGlicmFyeShjYXJldCkNCmNvX3Jlc3VsdCA8LSBjb25mdXNpb25NYXRyaXgocmVzdWx0LHBvc2l0aXZlPSIxIikNCnByaW50KGNvX3Jlc3VsdCkNCmBgYA0KDQohW10oaW1hZ2VzL1doYXRzQXBwJTIwSW1hZ2UlMjAyMDIzLTExLTMwJTIwYXQlMjAyMC4xNS4zNl9lOTVjMjVlMS5qcGcpDQoNCnRoaXMgbWF0cml4IGJlbG93IHNob3cgdGhlIGV2YWx1YXRpb24gbW9kZWwgb24gdGVzdGluZyBkYXRhDQoNCnwgRXZhbHVhdGlvbiBtZXRob2QgfCBNZXRob2QgIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLXwNCnwgQWNjdXJhY3kgICAgICAgICAgfCA4My44NCAlIHwNCnwgRXJyb3IgcmF0ZSAgICAgICAgfCAxNi4xNiUgIHwNCnwgU2Vuc2l0aXZpdHkgICAgICAgfCA4Ni43MyUgIHwNCnwgU3BlY2lmaWNpdHkgICAgICAgfCA4MS4wMCUgIHwNCnwgUHJlY2lzaW9uICAgICAgICAgfCA4MS43MyUgIHwNCg0KSW4gdGhpcyBwYXJ0aXRpb24gb2YgdGhlIGRlY2lzaW9uIHRyZWUsIHRoZSBhdmVyYWdlIGdyYWRlIHdpdGggdGhlIGhpZ2hlc3QgaW5mb3JtYXRpb24gZ2FpbiBpcyBzZWxlY3RlZCBhcyB0aGUgcm9vdCBhdHRyaWJ1dGUuIFRoZSBzZWxlY3Rpb24gaXMgYmFzZWQgb24gdGhlIGZhY3QgdGhhdCB0aGUgYXZlcmFnZSBncmFkZSBhdHRyaWJ1dGUgaGFzIHRoZSBsb3dlc3QgY29uZGl0aW9uYWwgZW50cm9weSwgdGhlIG1vZGVsIGFpbXMgdG8gY3JlYXRlIGEgbW9yZSBlZmZlY3RpdmUgc3BsaXQgYW5kIGltcHJvdmUgdGhlIGNsYXNzaWZpY2F0aW9uIGFjY3VyYWN5LiBhbHNvIHRvIG1lbnRpb24gdGhhdCBhdHRyaWJ1dGUgd2FzIG5vdCBpbmNsdWRlZCBpbiB0aGUgdHJlZSBnZW5kZXIscGFyZW50IHdhcyBpbiBjb2xsYWdlLCBwYXJlbnQgYWdlDQoNCk9uIGEgcG9zaXRpdmUgbm90ZSwgdGhlIGFjY3VyYWN5IGluIHRoaXMgcGFydGl0aW9uIGhhcyBpbmNyZWFzZWQgYnkgMS40NyUgY29tcGFyZWQgdG8gdGhlIHByZXZpb3VzIHBhcnRpdGlvbi4gVGhpcyBpbXByb3ZlbWVudCBpbiBhY2N1cmFjeSBjYW4gYmUgYXR0cmlidXRlZCB0byB0aGUgZmFjdCB0aGF0IHRoZSB0cmFpbmluZyBkYXRhIGhhcyBiZWVuIGluY3JlYXNlZCBieSAxMCUuIEJ5IGhhdmluZyBtb3JlIGRhdGEgZm9yIHRyYWluaW5nLCB0aGUgbW9kZWwgaGFzIGEgbGFyZ2VyIGFuZCBtb3JlIGRpdmVyc2Ugc2V0IG9mIGV4YW1wbGVzIHRvIGxlYXJuIGZyb20sIGFsbG93aW5nIGl0IHRvIG1ha2UgbW9yZSBhY2N1cmF0ZSBwcmVkaWN0aW9ucy4NCg0KIyMjIyAqKkdhaW4gcmF0aW8qKg0KDQpgYGB7cn0NCmxpYnJhcnkoUldla2EpDQoNCg0KQzQ1Rml0IDwtIEo0OCggd2lsbF9nb190b19jb2xsZWdlfi4sZGF0YT10cmFpbl9kYXRhKQ0KdGFibGUodHJhaW5fZGF0YSR3aWxsX2dvX3RvX2NvbGxlZ2UsIHByZWRpY3QoQzQ1Rml0KSkNCg0KcGxvdChDNDVGaXQsdHlwZSA9ICJzaW1wbGUiKQ0KdGVzdFByZWQgPC0gcHJlZGljdChDNDVGaXQsIG5ld2RhdGEgPSB0ZXN0X2RhdGEpDQpyZXN1bHRzIDwtIGNvbmZ1c2lvbk1hdHJpeCh0ZXN0UHJlZCwgdGVzdF9kYXRhJHdpbGxfZ29fdG9fY29sbGVnZSwgcG9zaXRpdmUgPSAiMSIpDQpwcmludChyZXN1bHRzKQ0KYGBgDQoNCiFbXShpbWFnZXMvV2hhdHNBcHAlMjBJbWFnZSUyMDIwMjMtMTEtMzAlMjBhdCUyMDIwLjE3LjI1X2MwMmExYjgxLmpwZykNCg0KdGhpcyBtYXRyaXggYmVsb3cgc2hvdyB0aGUgZXZhbHVhdGlvbiBtb2RlbCBvbiB0ZXN0aW5nIGRhdGENCg0KfCBFdmFsdWF0aW9uIG1ldGhvZCB8IE1ldGhvZCB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLXwNCnwgQWNjdXJhY3kgICAgICAgICAgfCA4MC4wMyUgfA0KfCBFcnJvciByYXRlICAgICAgICB8IDE5Ljk3JSB8DQp8IFNlbnNpdGl2aXR5ICAgICAgIHwgODQuNjklIHwNCnwgU3BlY2lmaWNpdHkgICAgICAgfCA3Ni4wMCUgfA0KfCBQcmVjaXNpb24gICAgICAgICB8IDc3LjU3JSB8DQoNCkluIHRoaXMgcGFydGljdWxhciBtb2RlbCB0aGF0IHV0aWxpemVzIHRoZSBnYWluIHJhdGlvLCBpdCBpcyBvYnNlcnZlZCB0aGF0IHRoZSBkZWNpc2lvbiB0cmVlIGlzIG9uY2UgYWdhaW4gbW9yZSBjb21wbGV4IGNvbXBhcmVkIHRvIHRoZSBvdGhlciBtb2RlbHMuIFRoZSBhdHRyaWJ1dGUgY2hvc2VuIGFzIHRoZSByb290IGF0dHJpYnV0ZSBpcyBwYXJlbnQgc2FsYXJ5LCBiYXNlZCBvbiBpdHMgaGlnaCBwdXJpdHkgYW5kIHJlc3VsdGluZyBoaWdoIGdhaW4gcmF0aW8uIEhvd2V2ZXIsIGl0IGlzIGltcG9ydGFudCB0byBub3RlIHRoYXQgdGhlIGdlbmRlciBhdHRyaWJ1dGUgd2FzIG5vdCBpbmNsdWRlZCBpbiB0aGUgZGVjaXNpb24gdHJlZS4gVWx0aW1hdGVseSwgdGhlIGdhaW4gcmF0aW8gbW9kZWwncyBjb25zdHJ1Y3Rpb24gaW52b2x2ZWQgY2FyZWZ1bCBjb25zaWRlcmF0aW9ucyBhbmQgcHJpb3JpdGl6ZWQgdGhlIHBhcmVudCdzIHNhbGFyeSBhcyByb290DQoNCiMjIyMgKipHaW5pIGluZGV4KioNCg0KYGBge3J9DQpsaWJyYXJ5KHJwYXJ0KQ0KbGlicmFyeShycGFydC5wbG90KQ0KY2FydF9maXQ9cnBhcnQod2lsbF9nb190b19jb2xsZWdlfi4sIGRhdGE9dHJhaW5fZGF0YSwgbWV0aG9kPSJjbGFzcyIsY3A9MC4wMDgpDQoNCg0KcnBhcnQucGxvdChjYXJ0X2ZpdCkNCg0KdGVzdFByZWQ8LSBwcmVkaWN0KGNhcnRfZml0LG5ld2RhdGE9dGVzdF9kYXRhLHR5cGU9ImNsYXNzIikNCnJlc3VsdHM8LSBjb25mdXNpb25NYXRyaXgodGVzdFByZWQsdGVzdF9kYXRhJHdpbGxfZ29fdG9fY29sbGVnZSxwb3NpdGl2ZT0iMSIpDQpwcmludChyZXN1bHRzKQ0KDQpgYGANCg0KdGhpcyBtYXRyaXggYmVsb3cgc2hvdyB0aGUgZXZhbHVhdGlvbiBtb2RlbCBvbiB0ZXN0aW5nIGRhdGENCg0KfCBFdmFsdWF0aW9uIG1ldGhvZCB8IE1ldGhvZCB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLXwNCnwgQWNjdXJhY3kgICAgICAgICAgfCA4MC44MSUgfA0KfCBFcnJvciByYXRlICAgICAgICB8IDE5LjE5JSB8DQp8IFNlbnNpdGl2aXR5ICAgICAgIHwgNzYuNTMlIHwNCnwgU3BlY2lmaWNpdHkgICAgICAgfCA4NS4wMCUgfA0KfCBQcmVjaXNpb24gICAgICAgICB8IDgzLjMzJSB8DQoNCmZvciB0aGUgZmlyc3QgdGltZSBob3VzZSBhcmVhIHdhcyBjaG9zZW4gYXMgdGhlIHJvb3QgZHVlIHRvIGl0cyBoaWdoIHB1cml0eSwgYXMgaW5kaWNhdGVkIGJ5IHRoZSBoaWdoIEdpbmkgaW5kZXguDQoNClRoZSBtb2RlbCByZWxpZXMgb24gdGhlIHBhcmVudF9zYWxhcnkgYXR0cmlidXRlIHRvIHNwbGl0IHRoZSB0cmVlIG11bHRpcGxlIHRpbWVzLCBpbmRpY2F0aW5nIGl0cyBpbXBvcnRhbmNlIGluIGRldGVybWluaW5nIHRoZSBjbGFzc2lmaWNhdGlvbi4gVGhpcyBzdWdnZXN0cyB0aGF0IHRoZSBwYXJlbnQncyBzYWxhcnkgaGFzIGEgc2lnbmlmaWNhbnQgaW1wYWN0IG9uIHdoZXRoZXIgYSBzdHVkZW50IHdpbGwgZ28gdG8gY29sbGVnZSBvciBub3QsIGFuZCBpdCBpcyByZXBlYXRlZGx5IHVzZWQgYXMgYSBjcml0ZXJpb24gZm9yIGZ1cnRoZXIgYnJhbmNoaW5nIGluIHRoZSB0cmVlLg0KDQpJdCdzIHdvcnRoIG5vdGluZyB0aGF0IDIwJSBvZiB0aGUgZGF0YXNldCBmb2xsb3dzIHRoZSBydWxlOg0KDQpJRiBob3VzZV9hcmVhIFw8IDAuNTQgYW5kIHBhcmVudF9zYWxhcnkgXDwgMC40MiwgVEhFTiB3aWxsIGdvIHRvIGNvbGxlZ2UgPSAxLg0KDQpUaGlzIHJ1bGUgaW1wbGllcyB0aGF0IHdoZW4gdGhlIGhvdXNlIGFyZWEgaXMgYmVsb3cgYSBjZXJ0YWluIHRocmVzaG9sZCBhbmQgdGhlIHBhcmVudCdzIHNhbGFyeSBpcyBiZWxvdyBhbm90aGVyIHRocmVzaG9sZCwgdGhlcmUgaXMgYSBoaWdoZXIgbGlrZWxpaG9vZCBvZiB0aGUgc3R1ZGVudCBnb2luZyB0byBjb2xsZWdlLg0KDQpNb3Jlb3ZlciwgMjglIG9mIHRoZSBkYXRhc2V0IGZvbGxvd3MgdGhlIHJ1bGU6DQoNCklGIGhvdXNlX2FyZWEgXD49IDAuNTQgYW5kIHBhcmVudF9zYWxhcnkgXD49IDAuMzcgYW5kIHR5cGVfc2Nob29sID0gMSwgVEhFTiB3aWxsIGdvIHRvIGNvbGxlZ2UgPSAxLiBUaGlzIHJ1bGUgc3VnZ2VzdHMgdGhhdCB3aGVuIHRoZSBob3VzZSBhcmVhIGlzIGFib3ZlIGEgY2VydGFpbiB0aHJlc2hvbGQsIHRoZSBwYXJlbnQncyBzYWxhcnkgaXMgYWJvdmUgYW5vdGhlciB0aHJlc2hvbGQsIGFuZCB0aGUgdHlwZSBvZiBzY2hvb2wgaXMgMSwgdGhlcmUgaXMgYSBoaWdoZXIgcHJvYmFiaWxpdHkgb2YgdGhlIHN0dWRlbnQgZ29pbmcgdG8gY29sbGVnZS4NCg0KT3ZlcmFsbCwgdGhlc2UgcnVsZXMgY292ZXIgYSBzaWduaWZpY2FudCBwb3J0aW9uIG9mIHRoZSBkYXRhc2V0LCA0OCUgb2YgaXQuIFRoaXMgaW5kaWNhdGVzIHRoYXQgYSBsYXJnZSBhbW91bnQgb2YgZGF0YSBjYW4gYmUgY2xhc3NpZmllZCBiYXNlZCBvbiB0aGVzZSBzcGVjaWZpYyBydWxlcywgcHJvdmlkaW5nIHZhbHVhYmxlIGluc2lnaHRzIGludG8gdGhlIGZhY3RvcnMgdGhhdCBpbmZsdWVuY2UgYSBzdHVkZW50J3MgbGlrZWxpaG9vZCBvZiBhdHRlbmRpbmcgY29sbGVnZS4gd2Ugb25seSBtZW50aW9uZWQgdGhvc2UgdHdvIHJ1bGVzIGR1byB0byB0aGVpciBoaWdoIHBlcmNlbnRhZ2Ugb2YgZGF0YQ0KDQp8IDUwJSB0cmFpbmluZywgNTAlIHRlc3RpbmcgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQppbmQ9c2FtcGxlKDIsIG5yb3coZGF0YSksIHJlcGxhY2U9VFJVRSwgcHJvYj1jKDAuNTAgLCAwLjUwKSkNCnRyYWluX2RhdGE9ZGF0YVtpbmQ9PTEsXQ0KdGVzdF9kYXRhPWRhdGFbaW5kPT0yLF0NCmRpbSh0cmFpbl9kYXRhKQ0KZGltKHRlc3RfZGF0YSkNCg0KYGBgDQoNCldlIHBhcnRpdGlvbiB0aGUgZGF0YSB0aGUgZGF0YSBpbnRvICg1MCUgdHJhaW5pbmcsIDUwJSB0ZXN0aW5nKS4gVGhpcyByZXN1bHQgaW4gNDkzIG9iamVjdCBpbiB0aGUgdHJhaW5pbmcgc2V0IGFuZCA1MDcgb2JqZWN0IGluIHRoZSB0ZXN0aW5nIHNldC4NCg0KIyMjIyAqKmluZm9ybWF0aW9uIGdhaW4qKg0KDQpgYGB7cn0NCmxpYnJhcnkocGFydHkpDQpteUZvcm11bGE8LSB3aWxsX2dvX3RvX2NvbGxlZ2V+ICsgdHlwZV9zY2hvb2wrc2Nob29sX2FjY3JlZGl0YXRpb24rZ2VuZGVyK2ludGVyZXN0K3Jlc2lkZW5jZStwYXJlbnRfYWdlK3BhcmVudF9zYWxhcnkraG91c2VfYXJlYSthdmVyYWdlX2dyYWRlcytwYXJlbnRfd2FzX2luX2NvbGxlZ2UNCg0KZGF0YXNldF9jdHJlZTwtY3RyZWUobXlGb3JtdWxhLCBkYXRhPXRyYWluX2RhdGEpDQp0YWJsZShwcmVkaWN0KGRhdGFzZXRfY3RyZWUpLCB0cmFpbl9kYXRhJHdpbGxfZ29fdG9fY29sbGVnZSkgDQoNCnBsb3QoZGF0YXNldF9jdHJlZSx0eXBlPSJzaW1wbGUiKQ0KdGVzdFByZWQgPC0gcHJlZGljdChkYXRhc2V0X2N0cmVlLCBuZXdkYXRhID0gdGVzdF9kYXRhKQ0KcmVzdWx0PC10YWJsZSh0ZXN0UHJlZCwgdGVzdF9kYXRhJHdpbGxfZ29fdG9fY29sbGVnZSApDQoNCmxpYnJhcnkoY2FyZXQpDQpjb19yZXN1bHQgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdCwgcG9zaXRpdmUgPSAiMSIpDQpwcmludChjb19yZXN1bHQpDQoNCmBgYA0KDQohW10oaW1hZ2VzL1doYXRzQXBwJTIwSW1hZ2UlMjAyMDIzLTExLTMwJTIwYXQlMjAyMC4yMC4wM19iYTBiM2ZjYi5qcGcpDQoNCnRoaXMgbWF0cml4IGJlbG93IHNob3cgdGhlIGV2YWx1YXRpb24gbW9kZWwgb24gdGVzdGluZyBkYXRhDQoNCnwgRXZhbHVhdGlvbiBtZXRob2QgfCBNZXRob2QgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS18DQp8IEFjY3VyYWN5ICAgICAgICAgIHwgODQuMjIlIHwNCnwgRXJyb3IgcmF0ZSAgICAgICAgfCAxNS43OCUgfA0KfCBTZW5zaXRpdml0eSAgICAgICB8IDg5LjUzJSB8DQp8IFNwZWNpZmljaXR5ICAgICAgIHwgNzguNzElIHwNCnwgUHJlY2lzaW9uICAgICAgICAgfCA4MS4zNCUgfA0KDQpJbiB0aGlzIGRlY2lzaW9uIHRyZWUgbW9kZWwsIHRoZSByb290IGF0dHJpYnV0ZSBpcyBkZXRlcm1pbmVkIGJ5IHNlbGVjdGluZyB0aGUgYXZlcmFnZSBncmFkZSB3aXRoIHRoZSBoaWdoZXN0IGluZm9ybWF0aW9uIGdhaW4uIFRoaXMgaW5kaWNhdGVzIHRoYXQgdGhlIGF2ZXJhZ2UgZ3JhZGUgaXMgY29uc2lkZXJlZCB0aGUgbW9zdCBpbmZvcm1hdGl2ZSBmZWF0dXJlIGZvciBjbGFzc2lmeWluZyB0aGUgZGF0YSwgYXMgaXQgcHJvdmlkZXMgc2lnbmlmaWNhbnQgaW5zaWdodHMgaW50byB0aGUgY2xhc3NpZmljYXRpb24gcHJvY2Vzcy4NCg0KVG8gaW1wcm92ZSB0aGUgbW9kZWwncyBwcmVkaWN0aXZlIGNhcGFiaWxpdHksIGFkZGl0aW9uYWwgc3BsaXR0aW5nIGNyaXRlcmlhIGFyZSBpbmNvcnBvcmF0ZWQgYmFzZWQgb24gdGhlaXIgaW5mb3JtYXRpb24gZ2FpbiB2YWx1ZXMuIFRoZXNlIGNyaXRlcmlhIGluY2x1ZGUgdHlwZV9zY2hvb2wsIGludGVyZXN0LCByZXNpZGVuY2UsIHBhcmVudF9zYWxhcnksIGhvdXNlX2FyZWEsIGFuZCB3aGV0aGVyIHRoZSBwYXJlbnQgd2FzIGluIGNvbGxlZ2UuIEJ5IGNvbnNpZGVyaW5nIHRoZXNlIGF0dHJpYnV0ZXMsIHRoZSBtb2RlbCBhaW1zIHRvIGNhcHR1cmUgbW9yZSByZWxldmFudCBpbmZvcm1hdGlvbiBhbmQgZW5oYW5jZSBpdHMgYWJpbGl0eSB0byBjbGFzc2lmeSB0aGUgZGF0YSBhY2N1cmF0ZWx5Lg0KDQpJbiB0aGUgdHJlZSBzdHJ1Y3R1cmUsIHRoZSBtb2RlbCB1dGlsaXplcyBib3RoIHRoZSBob3VzZSBhcmVhIGFuZCBhdmVyYWdlIGdyYWRlcyBhcyBzcGxpdHRpbmcgY3JpdGVyaWEgaW4gbXVsdGlwbGUgbGV2ZWxzLiBUaGlzIG1lYW5zIHRoYXQgdGhlIGRhdGEgaXMgZGl2aWRlZCBpbnRvIGRpZmZlcmVudCBicmFuY2hlcyBvZiB0aGUgdHJlZSBiYXNlZCBvbiB0aGVzZSBhdHRyaWJ1dGVzLCBhbGxvd2luZyBmb3IgbW9yZSByZWZpbmVkIGNsYXNzaWZpY2F0aW9uLg0KDQpnZW5kZXIsIHBhcmVudCBhZ2UsIGFuZCBzY2hvb2wgYWNjcmVkaXRhdGlvbiBhcmUgbm90IGluY2x1ZGVkIGluIHRoZSB0cmVlIGR1ZSB0byBlaXRoZXIgbGltaXRlZCBhdmFpbGFiaWxpdHkgb2YgdGhlIGRhdGEgb3IgdGhlaXIgbG93ZXIgaW5mb3JtYXRpb24gZ2FpbiB2YWx1ZXMsIGluZGljYXRpbmcgdGhhdCB0aGV5IG1heSBoYXZlIGxlc3MgaW1wYWN0IG9uIHRoZSBjbGFzc2lmaWNhdGlvbiBwcm9jZXNzIGFjY29yZGluZyB0byB0aGUgbW9kZWwncyBldmFsdWF0aW9uLg0KDQojIyMjICoqR2FpbiByYXRpbyoqDQoNCmBgYHtyfQ0KbGlicmFyeShSV2VrYSkNCkM0NUZpdCA8LSBKNDgoIHdpbGxfZ29fdG9fY29sbGVnZX4uLGRhdGE9dHJhaW5fZGF0YSkNCnRhYmxlKHRyYWluX2RhdGEkd2lsbF9nb190b19jb2xsZWdlLCBwcmVkaWN0KEM0NUZpdCkpDQpwbG90KEM0NUZpdCx0eXBlID0gInNpbXBsZSIpDQp0ZXN0UHJlZCA8LSBwcmVkaWN0KEM0NUZpdCwgbmV3ZGF0YSA9IHRlc3RfZGF0YSkNCnJlc3VsdHMgPC0gY29uZnVzaW9uTWF0cml4KHRlc3RQcmVkLCB0ZXN0X2RhdGEkd2lsbF9nb190b19jb2xsZWdlLCBwb3NpdGl2ZSA9ICIxIikNCnByaW50KHJlc3VsdHMpDQoNCmBgYA0KDQohW10oaW1hZ2VzL1doYXRzQXBwJTIwSW1hZ2UlMjAyMDIzLTExLTMwJTIwYXQlMjAyMC4yMC4zM19hMzgxYjEwYS5qcGcpDQoNCnRoaXMgbWF0cml4IGJlbG93IHNob3cgdGhlIGV2YWx1YXRpb24gbW9kZWwgb24gdGVzdGluZyBkYXRhDQoNCnwgRXZhbHVhdGlvbiBtZXRob2QgfCBNZXRob2QgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS18DQp8IEFjY3VyYWN5ICAgICAgICAgIHwgODUuMjElIHwNCnwgRXJyb3IgcmF0ZSAgICAgICAgfCAxNC43OSUgfA0KfCBTZW5zaXRpdml0eSAgICAgICB8IDg3LjYwJSB8DQp8IFNwZWNpZmljaXR5ICAgICAgIHwgODIuNzMlIHwNCnwgUHJlY2lzaW9uICAgICAgICAgfCA4NC4wMSUgfA0KDQpJbiBjb250cmFzdCB0byB0aGUgcHJldmlvdXMgZGVjaXNpb24gdHJlZSwgdGhlIGN1cnJlbnQgdHJlZSBpcyBtb3JlIGNvbXBsZXggYW5kIGluY2x1ZGVzIGFsbCB0aGUgYXR0cmlidXRlcyBleGNlcHQgZm9yIHBhcmVudCBhZ2UuIE5vdGFibHksIGZvciB0aGUgZmlyc3QgdGltZSwgZ2VuZGVyIGlzIGluY2x1ZGVkIGFzIGFuIGF0dHJpYnV0ZSBpbiB0aGUgdHJlZS4gVGhpcyBpbmRpY2F0ZXMgdGhhdCB0aGUgZGVjaXNpb24gdHJlZSBub3cgdGFrZXMgaW50byBhY2NvdW50IGEgd2lkZXIgcmFuZ2Ugb2YgZmFjdG9ycywgaW5jbHVkaW5nIGdlbmRlciwgdG8gbWFrZSBpdHMgY2xhc3NpZmljYXRpb25zLiBCeSBpbmNvcnBvcmF0aW5nIHRoZXNlIGFkZGl0aW9uYWwgYXR0cmlidXRlcywgdGhlIG1vZGVsIGFpbXMgdG8gY2FwdHVyZSBtb3JlIG51YW5jZWQgcGF0dGVybnMgYW5kIHJlbGF0aW9uc2hpcHMgaW4gdGhlIGRhdGEuDQoNClRoZSBhdHRyaWJ1dGUgd2l0aCB0aGUgaGlnaGVzdCBHYWluIHJhdGlvIGlzIGNob3NlbiBhcyB0aGUgcm9vdCBhdHRyaWJ1dGUuIEluIHRoaXMgcGFydGljdWxhciBjYXNlLCBQYXJlbnQgc2FsYXJ5IGlzIGRldGVybWluZWQgdG8gaGF2ZSB0aGUgaGlnaGVzdCBHYWluIHJhdGlvIGFuZCBpcyBzZWxlY3RlZCBhcyB0aGUgcm9vdCBhdHRyaWJ1dGUuIFRoaXMgc3VnZ2VzdHMgdGhhdCBQYXJlbnQgc2FsYXJ5IHByb3ZpZGVzIHRoZSBtb3N0IHNpZ25pZmljYW50IHJlZHVjdGlvbiBpbiB1bmNlcnRhaW50eSBhbmQgaXMgY29uc2lkZXJlZCBjcnVjaWFsIGZvciBjbGFzc2lmeWluZyB0aGUgZGF0YSBhY2N1cmF0ZWx5Lg0KDQojIyMjICoqR2luaSBpbmRleCoqDQoNCmBgYHtyfQ0KbGlicmFyeShycGFydCkNCmxpYnJhcnkocnBhcnQucGxvdCkNCmNhcnRfZml0PXJwYXJ0KHdpbGxfZ29fdG9fY29sbGVnZX4uLCBkYXRhPXRyYWluX2RhdGEsIG1ldGhvZD0iY2xhc3MiLGNwPTAuMDA4KQ0KDQpycGFydC5wbG90KGNhcnRfZml0KQ0KDQp0ZXN0UHJlZDwtIHByZWRpY3QoY2FydF9maXQsbmV3ZGF0YT10ZXN0X2RhdGEsdHlwZT0iY2xhc3MiKQ0KcmVzdWx0czwtIGNvbmZ1c2lvbk1hdHJpeCh0ZXN0UHJlZCx0ZXN0X2RhdGEkd2lsbF9nb190b19jb2xsZWdlLHBvc2l0aXZlPSIxIikNCnByaW50KHJlc3VsdHMpDQpgYGANCg0KdGhpcyBtYXRyaXggYmVsb3cgc2hvdyB0aGUgZXZhbHVhdGlvbiBtb2RlbCBvbiB0ZXN0aW5nIGRhdGENCg0KfCBFdmFsdWF0aW9uIG1ldGhvZCB8IE1ldGhvZCB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLXwNCnwgQWNjdXJhY3kgICAgICAgICAgfCA4NS4yMSUgfA0KfCBFcnJvciByYXRlICAgICAgICB8IDI0Ljc5JSB8DQp8IFNlbnNpdGl2aXR5ICAgICAgIHwgODguMzclIHwNCnwgU3BlY2lmaWNpdHkgICAgICAgfCA4MS45MyUgfA0KfCBQcmVjaXNpb24gICAgICAgICB8IDgzLjUyJSB8DQoNCjogVGhlIGF0dHJpYnV0ZSBjaG9zZW4gYXMgdGhlIHJvb3QgYXR0cmlidXRlIGluIHRoaXMgZGVjaXNpb24gdHJlZSBpcyBhdmVyYWdlIGdyYWRlcywgcHJpbWFyaWx5IGR1ZSB0byBpdHMgaGlnaCBwdXJpdHksIGluZGljYXRpbmcgYSBoaWdoIEdpbmkgaW5kZXguIFRoZSBHaW5pIGluZGV4IG1lYXN1cmVzIHRoZSBpbXB1cml0eSBvZiBhIG5vZGUgaW4gYSBkZWNpc2lvbiB0cmVlLCBhbmQgYSBoaWdoIHB1cml0eSB2YWx1ZSBzdWdnZXN0cyB0aGF0IHRoZSBhdHRyaWJ1dGUgaXMgZWZmZWN0aXZlIGZvciBjbGFzc2lmaWNhdGlvbi4NCg0KMjclIG9mIHRoZSBkYXRhc2V0IGZvbGxvd3MgdGhlIHJ1bGU6DQoNCklGIGF2ZXJhZ2VfZ3JhZGVzPStBLCtCLEEgYW5kIHBhcmVudF9zYWxhcnlcPj0wLjM3IGFuZCBob3VzZV9hcmVhXD49MC40MSBhbmQgdHlwZV9zY2hvb2w9MSwgVEhFTiB3aWxsX2dvX3RvX2NvbGxhZ2U9MC4NCg0KRnVydGhlcm1vcmUsIDE3JSBvZiB0aGUgZGF0YXNldCBmb2xsb3dzIHRoZSBydWxlOg0KDQpJRiBhdmVyYWdlX2dyYWRlcz1CLCtDIGFuZCBob3VzZV9hcmVhXDw9MC40MSwgVEhFTiB3aWxsX2dvX3RvX2NvbGxhZ2U9MS4NCg0KVGhlc2UgcnVsZXMgaGlnaGxpZ2h0IHRoZSBjb21wbGV4aXR5IGFuZCB2YXJpYWJpbGl0eSBpbiBkZXRlcm1pbmluZyB3aGV0aGVyIGEgc3R1ZGVudCB3aWxsIGdvIHRvIGNvbGxlZ2UuIEV2ZW4gaWYgYSBzdHVkZW50IGhhcyBzdGFibGUgZmluYW5jaWFsIGNpcmN1bXN0YW5jZXMgYW5kIGdvb2QgZ3JhZGVzLCBpdCBkb2VzIG5vdCBndWFyYW50ZWUgdGhlaXIgZW5yb2xsbWVudCBpbiBjb2xsZWdlLiBpbiBjb250cmFjdCB0byB0aGUgc2Vjb25kIHdoaWNoIG1lYW4gT3RoZXIgZmFjdG9ycyBjb3VsZCBwbGF5IGEgcm9sZSBpbiB0aGUgZGVjaXNpb24tbWFraW5nIHByb2Nlc3MuDQoNCndlIG9ubHkgbWVudGlvbmVkIHRob3NlIHR3byBydWxlcyBkdW8gdG8gdGhlaXIgaGlnaCBwZXJjZW50YWdlIG9mIGRhdGENCg0KIyMgZmluYWwgYW5hbHlzaXMNCg0KIyMjIyBpbmZvcm1hdGlvbiBnYWluDQoNCkluZm9ybWF0aW9uIEdhaW4gaXMgYSBtZWFzdXJlIHVzZWQgaW4gdGhlIElEMyBhbGdvcml0aG0gZm9yIGF0dHJpYnV0ZSBzZWxlY3Rpb24gaW4gZGVjaXNpb24gdHJlZSBsZWFybmluZy4gSXQgcXVhbnRpZmllcyB0aGUgYW1vdW50IG9mIGluZm9ybWF0aW9uIG9idGFpbmVkIGFib3V0IHRoZSBjbGFzcyB2YXJpYWJsZSB3aGVuIGEgZ2l2ZW4gYXR0cmlidXRlIGlzIHVzZWQgdG8gc3BsaXQgdGhlIGRhdGEuIFRoZSBJbmZvcm1hdGlvbiBHYWluIGlzIGNhbGN1bGF0ZWQgYnkgc3Vic3RyYWN0IHRoZSBlbnRyb3B5IEV4cGVjdGVkIGluZm9ybWF0aW9uIG5lZWRlZCB0byBjbGFzc2lmeSBhIHR1cGxlIGluIEQgYSB3aXRoIENvbmRpdGlvbmFsIEVudHJvcHkgSW5mb3JtYXRpb24gbmVlZGVkIGFmdGVyIHVzaW5nIEEgdG8gc3BsaXQgRCBpbnRvIHYgcGFydGl0aW9ucywgdG8gY2xhc3NpZnkgRCAuIEEgaGlnaGVyIEluZm9ybWF0aW9uIEdhaW4gaW5kaWNhdGVzIHRoYXQgdGhlIGF0dHJpYnV0ZSBpcyBtb3JlIGluZm9ybWF0aXZlIGFuZCBzaG91bGQgYmUgY2hvc2VuIGFzIHRoZSBzcGxpdHRpbmcgY3JpdGVyaW9uLg0KDQpmb3Igb3VyIHRyZWVzLA0KDQphdmVyYWdlIGdyYWRlIHdhcyBjaG9zZW4gYXMgdGhlIHJvb3QgYXR0cmlidXRlIGZvciBhbGwgMyB0cmVlcyB3aXRoIHRoZSBoaWdoZXN0IGluZm9ybWF0aW9uIGdhaW4gVGhpcyBhdHRyaWJ1dGUgUmVmbGVjdHMgdGhlIGxlYXN0IHJhbmRvbW5lc3Mgb3IgImltcHVyaXR5IiBpbiB0aGVzZSBwYXJ0aXRpb25zIGFuZCBndWFyYW50ZWVzIHRoYXQgYSBzaW1wbGUgdHJlZSBpcyBmb3VuZC4NCg0KfCAgICAgICAgICAgICB8IDUwJS01MCUgfCA3MCUtMzAlIHwgODAlLTIwJSB8DQp8LS0tLS0tLS0tLS0tLXwtLS0tLS0tLS18LS0tLS0tLS0tfC0tLS0tLS0tLXwNCnwgQWNjdXJhY3kgICAgfCA4NC4yMiUgIHwgODIuMzcgJSB8IDgzLjg0ICUgfA0KfCBFcnJvciByYXRlICB8IDE1Ljc4JSAgfCAxNy42MyUgIHwgMTYuMTYlICB8DQp8IFNlbnNpdGl2aXR5IHwgODkuNTMlICB8IDc1Ljg5JSAgfCA4Ni43MyUgIHwNCnwgU3BlY2lmaWNpdHkgfCA3OC43MSUgIHwgODguMzElICB8IDgxLjAwJSAgfA0KfCBQcmVjaXNpb24gICB8IDgxLjM0JSAgfCA4NS42MCUgIHwgODEuNzMlICB8DQoNCnRoZSBldmFsdWF0aW9uIG1ldGhvZCB0YWJsZSBzaG93cyB0aGF0IHRoZSBiZXN0IHBhcnRpdG9uIGZvciBpbmZvcm1hdGlvbiBnYWluIHNob3dzIHRoYXQgNTAlLTUwJSBpcyB0aGUgYmVzdCBtb2RlbCBiYXNlZCBvbiBhY2N1cmFjeSh0dXBsZXMgdGhhdCBhcmUgY29ycmVjdGx5IGNsYXNzaWZpZWQpIHdpdGggcHJlY250YWdlcyA4NC4yMiUgYW5kIFNlbnNpdGl2aXR5IChUcnVlIHBvc2l0aXZlIHJlY29nbml0aW9uIHJhdGUpIHdpdGggODkuNTMlIGJ1dCB0aGUgbG93ZXN0IFNwZWNpZmljaXR5KFRydWUgbmVnYXRpdmUgcmVjb2duaXRpb24gcmF0ZSkgd2l0aCA3OC43MSUuIG92ZXJhbGwgaXQncyB0aGUgYmVzdCBtb2RlbCBmb3IgaW5mb3JtYXRpb24gZ2FpbiBoYXMgdGhlIGxvd2VzdCBlcnJvciByYXRlIDE1Ljc4JQ0KDQojIyMjIGdhaW4gcmF0aW8NCg0KR2FpbiBSYXRpbyBpcyBhbiBub3JtYWxpenRpb24gZm9yIEluZm9ybWF0aW9uIEdhaW4gYW5kIGlzIHVzZWQgaW4gdGhlIEM0LjUgYWxnb3JpdGhtLCB3aGljaCBpcyBhbiBleHRlbnNpb24gb2YgSUQzLiBXaGlsZSBJbmZvcm1hdGlvbiBHYWluIHRlbmRzIHRvIGZhdm9yIGF0dHJpYnV0ZXMgd2l0aCBtYW55IG11bHRpdmFyaWF0ZSBJdCBpcyBjYWxjdWxhdGVkIGJ5IGRpdmlkaW5nIHRoZSBJbmZvcm1hdGlvbiBHYWluIGJ5IFNwbGl0SW5mbyBhKEQpLg0KDQpmb3Igb3VyIHRyZWVzLA0KDQpQYXJlbnQgc2FsYXJ5IHdhcyBjaG9zZW4gYXMgdGhlIHJvb3QgYXR0cmlidXRlIGZvciBhbGwgMyB0cmVlcyBkdWUgdG8gaXRzIGhpZ2ggR2FpbiByYXRpbywgdGhlIHRyZWVzIHNwbGl0IGFyZSB1bmJhbGFuY2VkIHNpbmNlIGdhaW4gcmF0aW8gVGVuZHMgdG8gcHJlZmVyIHVuYmFsYW5jZWQgc3BsaXRzIGluIHdoaWNoIG9uZSBwYXJ0aXRpb24gaXMgbXVjaCBzbWFsbGVyIHRoYW4gdGhlIG90aGVycw0KDQp8ICAgICAgICAgICAgIHwgNTAlLTUwJSB8IDcwJS0zMCUgfCA4MCUtMjAlIHwNCnwtLS0tLS0tLS0tLS0tfC0tLS0tLS0tLXwtLS0tLS0tLS18LS0tLS0tLS0tfA0KfCBBY2N1cmFjeSAgICB8IDg1LjIxJSAgfCA4MC4zNCUgIHwgODAuMDMlICB8DQp8IEVycm9yIHJhdGUgIHwgMTQuNzklICB8IDE5LjY2JSAgfCAxOS45NyUgIHwNCnwgU2Vuc2l0aXZpdHkgfCA4Ny42MCUgIHwgNzcuMzAlICB8IDg0LjY5JSAgfA0KfCBTcGVjaWZpY2l0eSB8IDgyLjczJSAgfCA4My4xMiUgIHwgNzYuMDAlICB8DQp8IFByZWNpc2lvbiAgIHwgODQuMDElICB8IDgwLjc0JSAgfCA3Ny41NyUgIHwNCg0KdGhlIGV2YWx1YXRpb24gbWV0aG9kIHRhYmxlIHNob3dzIHRoYXQgdGhlIGJlc3QgcGFydGl0aW9uIGZvciBnYWluIHJhdGlvIHNob3dzIHRoYXQgNTAlLTUwJSBpcyB0aGUgYmVzdCBtb2RlbCBiYXNlZCBvbiBhY2N1cmFjeSh0dXBsZXMgdGhhdCBhcmUgY29ycmVjdGx5IGNsYXNzaWZpZWQpIHdpdGggcGVyY250YWdlcyA4NS4yMSUgYW5kIFNlbnNpdGl2aXR5IChUcnVlIHBvc2l0aXZlIHJlY29nbml0aW9uIHJhdGUpIHdpdGggODcuNjAlIGFuZCBQcmVjaXNpb24odHVwbGVzIGxhYmVsZWQgYXMgcG9zaXRpdmUgYXJlIGFjdHVhbGx5IHBvc2l0aXZlKSB3aXRoIHBlcmNlbnRhZ2VzIDg0LjAxJSBidXQgdGhlIGxvd2VzdCBTcGVjaWZpY2l0eShUcnVlIG5lZ2F0aXZlIHJlY29nbml0aW9uIHJhdGUpIHdpdGggODIuNzMlLiBvdmVyYWxsIGl0J3MgdGhlIGJlc3QgbW9kZWwgZG9yIGdhaW4gcmF0aW8gd2l0aCB0aGUgbG93ZXN0IGVycm9yIHJhdGUgMTQuNzklDQoNCiMjIyMgZ2luaSBpbmRleA0KDQpHaW5pIEluZGV4IGlzIGEgY3JpdGVyaW9uIHVzZWQgaW4gdGhlIENBUlQgKENsYXNzaWZpY2F0aW9uIGFuZCBSZWdyZXNzaW9uIFRyZWVzKSBhbGdvcml0aG0gZm9yIGF0dHJpYnV0ZSBzZWxlY3Rpb24uIEl0IG1lYXN1cmVzIHRoZSBpbXB1cml0eSBvZiBhIHNldCBvZiB0dXBsZXMsIHdpdGggbG93ZXIgdmFsdWVzIEdpbmkgYShEKSBpbmRpY2F0aW5nIGhpZ2hlciBwdXJpdHkuIFRoZSBHaW5pIEluZGV4IGlzIGNhbGN1bGF0ZWQgYnkgc3VtbWluZyB0aGUgc3F1YXJlZCBwcm9iYWJpbGl0aWVzIG9mIGVhY2ggY2xhc3MgbGFiZWwgaW4gdGhlIHNldA0KDQpmb3Igb3VyIHRyZWVzLA0KDQpnaW5pIGluZGV4IGhhZCBkaWZmZXJlbnQgcm9vdCAocGFyZW50IHNhbGFyeS9ob3VzZSBhcmVhL2F2ZXJhZ2VzIGdyYWRlcykgZm9yIGVhY2ggdHJlZQ0KDQp8ICAgICAgICAgICAgIHwgNTAlLTUwJSB8IDcwJS0zMCUgfCA4MCUtMjAlIHwNCnwtLS0tLS0tLS0tLS0tfC0tLS0tLS0tLXwtLS0tLS0tLS18LS0tLS0tLS0tfA0KfCBBY2N1cmFjeSAgICB8IDg1LjIxJSAgfCA3OS42NiUgIHwgODAuODElICB8DQp8IEVycm9yIHJhdGUgIHwgMTQuNzklICB8IDIwLjM0JSAgfCAxOS4xOSUgIHwNCnwgU2Vuc2l0aXZpdHkgfCA4OC4zNyUgIHwgNzguMDElICB8IDc2LjUzJSAgfA0KfCBTcGVjaWZpY2l0eSB8IDgxLjkzJSAgfCA4MS4xNyUgIHwgODUuMDAlICB8DQp8IFByZWNpc2lvbiAgIHwgODMuNTIlICB8IDc5LjE0JSAgfCA4My4zMyUgIHwNCg0KdGhlIGV2YWx1YXRpb24gbWV0aG9kIHRhYmxlIHNob3dzIHRoYXQgdGhlIGJlc3QgcGFydGl0aW9uIGZvciBnYWluIHJhdGlvIHNob3dzIHRoYXQgNTAlLTUwJSBpcyB0aGUgYmVzdCBtb2RlbCBiYXNlZCBvbiBhY2N1cmFjeSh0dXBsZXMgdGhhdCBhcmUgY29ycmVjdGx5IGNsYXNzaWZpZWQpIHdpdGggcGVyY2VudGFnZXMgODUuMjElIGFuZCBTZW5zaXRpdml0eSAoVHJ1ZSBwb3NpdGl2ZSByZWNvZ25pdGlvbiByYXRlKSB3aXRoIDg4LjM3JSBhbmQgUHJlY2lzaW9uKHR1cGxlcyBsYWJlbGVkIGFzIHBvc2l0aXZlIGFyZSBhY3R1YWxseSBwb3NpdGl2ZSkgd2l0aCBwZXJjZW50YWdlcyA4My41MiUgb3ZlcmFsbCBpdCdzIHRoZSBiZXN0IG1vZGVsIGZvciBnaW5pIGluZGV4IHdpdGggdGhlIGxvd2VzdCBlcnJvciByYXRlIDE0Ljc5JQ0KDQoqKm5vdGljZWFibGUgbm90ZXMqKg0KDQppbXBvcnRhbnQgdG8gbm90ZSB0aGF0IGdlbmRlciB3YXMgbm90IGluY2x1ZGVkIGluIDcgb2YgOSB0cmVlcyB3aGljaA0KDQpwYXJlbnQncyBzYWxhcnkgd2FzIHRoZSByb290IG9mIDQgb2YgOSB0cmVlcw0KDQphdmVyYWdlIGdyYWRlcyB3YXMgdGhlIHJvb3Qgb2YgNCBvZiA5IHRyZWVzDQoNCmhvdXNlIGFyZWEgd2FzIHRoZSByb290IG9ubHkgb25jZSAxIG9mIDkgdHJlZQ0KDQpJbiBzdW1tYXJ5LCB0aGUgYW5hbHlzaXMgb2YgdGhlIGRlY2lzaW9uIHRyZWVzIHJldmVhbHMgdGhhdCBmaW5hbmNpYWwgY2lyY3Vtc3RhbmNlcywgYXMgaW5kaWNhdGVkIGJ5IHBhcmVudCdzIHNhbGFyeSwgYW5kIGFjYWRlbWljIHBlcmZvcm1hbmNlLCBhcyBpbmRpY2F0ZWQgYnkgYXZlcmFnZSBncmFkZXMsIGFyZSBjcnVjaWFsIGZhY3RvcnMgZm9yIGNvbnRlbXBvcmFyeSB1bml2ZXJzaXRpZXMgd2hlbiBwcmVkaWN0aW5nIGNvbGxlZ2UgZW5yb2xsbWVudC4gSW4gdGhpcyBwYXJ0aWN1bGFyIGRhdGFzZXQsIGdlbmRlciBpcyBub3QgY29uc2lkZXJlZCBoaWdobHkgaW1wb3J0YW50IGluIHRoZSBkZWNpc2lvbi1tYWtpbmcgcHJvY2Vzcy4NCg0KIyMjIyBiZXN0IG1vZGVsDQoNCnRoZSA1MCUgdHJhaW5pbmcsIDUwJSB0ZXN0aW5nIHdhcyB0aGUgYmVzdCBwYXJ0aXRpb24gZm9yIGFsbCBhdHRyaWJ1dGUgc2VsZWN0aW9uIG1lYXN1cmVzIHdpdGggYWNjdXJhY2llcyA4NS4yMSUuIGZvciBib3RoIGdpbmkgaW5kZXggYW5kIGdhaW4gcmF0aW8NCg0KQm90aCBhdHRyaWJ1dGUgc2VsZWN0aW9uIG1lYXN1cmVzIHlpZWxkIHRoZSBzYW1lIGFjY3VyYWN5IGFuZCBlcnJvciByYXRlLCBzbyB3ZSBuZWVkIHRvIGNoZWNrIHRoZSByZW1haW5pbmcgZXZhbHVhdGlvbiBtZXRyaWNzIHRoYXQgaXMgcHJvdmlkZWQ6DQoNCnwgICAgICAgICAgICAgfCBnYWluIHJhdGlvIHwgZ2luaSBpbmRleCB8DQp8LS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tfA0KfCBBY2N1cmFjeSAgICB8IDg1LjIxJSAgICAgfCA4NS4yMSUgICAgIHwNCnwgRXJyb3IgcmF0ZSAgfCAxNC43OSUgICAgIHwgMTQuNzklICAgICB8DQp8IFNlbnNpdGl2aXR5IHwgODcuNjAlICAgICB8IDg4LjM3JSAgICAgfA0KfCBTcGVjaWZpY2l0eSB8IDgyLjczJSAgICAgfCA4MS45MyUgICAgIHwNCnwgUHJlY2lzaW9uICAgfCA4NC4wMSUgICAgIHwgODMuNTIlICAgICB8DQoNCkNvbXBhcmluZyB0aGUgc2Vuc2l0aXZpdHksIHNwZWNpZmljaXR5LCBhbmQgcHJlY2lzaW9uOg0KDQpcLSBTZW5zaXRpdml0eTogR2luaSBJbmRleCBoYXMgYSBoaWdoZXIgc2Vuc2l0aXZpdHkgdmFsdWUgKDg4LjM3JSkgY29tcGFyZWQgdG8gR2FpbiBSYXRpbyAoODcuNjAlKS4gSGlnaGVyIHNlbnNpdGl2aXR5IGluZGljYXRlcyBhIGJldHRlciBhYmlsaXR5IHRvIGNvcnJlY3RseSBpZGVudGlmeSBwb3NpdGl2ZSBpbnN0YW5jZXMgc28gKipnaW5pIGluZGV4IGlzIGJldHRlciByZWdhcmRpbmcgU2Vuc2l0aXZpdHkuKioNCg0KXC0gU3BlY2lmaWNpdHk6IEdpbmkgSW5kZXggaGFzIGEgbG93ZXIgc3BlY2lmaWNpdHkgdmFsdWUgKDgxLjkzJSkgY29tcGFyZWQgdG8gR2FpbiBSYXRpbyAoODIuNzMlKS4gSGlnaGVyIHNwZWNpZmljaXR5IGluZGljYXRlcyBhIGJldHRlciBhYmlsaXR5IHRvIGNvcnJlY3RseSBpZGVudGlmeSBuZWdhdGl2ZSBpbnN0YW5jZXMuDQoNClwtIFByZWNpc2lvbjogR2FpbiBSYXRpbyBoYXMgYSBoaWdoZXIgcHJlY2lzaW9uIHZhbHVlICg4NC4wMSUpIGNvbXBhcmVkIHRvIEdpbmkgSW5kZXggKDgzLjUyJSkuIEhpZ2hlciBwcmVjaXNpb24gaW5kaWNhdGVzIGEgYmV0dGVyIGFiaWxpdHkgdG8gY29ycmVjdGx5IGNsYXNzaWZ5IHBvc2l0aXZlIGluc3RhbmNlcy4NCg0KKipzbyBnYWluIHJhdGlvIGlzIGJldHRlciByZWdhcmRpbmcqKiAqKlNwZWNpZmljaXR5IGFuZCBQcmVjaXNpb24gLioqDQoNCkJhc2VkIG9uIHRoZXNlIGNvbXBhcmlzb25zLCBpdCBpcyBkaWZmaWN1bHQgdG8gZGV0ZXJtaW5lIGRlZmluaXRpdmVseSB3aGljaCBhdHRyaWJ1dGUgc2VsZWN0aW9uIG1lYXN1cmUgaXMgdGhlIGJlc3QuIFRoZSBjaG9pY2UgYmV0d2VlbiBHaW5pIEluZGV4IGFuZCBHYWluIFJhdGlvIG1heSBkZXBlbmQgb24gdGhlIHNwZWNpZmljIHJlcXVpcmVtZW50cyBhbmQgcHJpb3JpdGllcyBvZiB0aGUgcHJvYmxlbSBidXQgc2luY2Ugd2UgZm9jdXMgb24gdGhlIHRydWUgcG9zaXRpdmUgbW9yZSB0aGFuIHRydWUgbmVnYXRpdmUgYmVjYXVzZSBvdXIgZ29hbCBvZiBjbGFzc2lmaWNhdGlvbiB0byBkZXRlcm1pbmUgd2hvIHdpbGwgZ28gdG8gY29sbGFnZSwgc28gd2Ugd2lsbCBjaG9vc2UgdGhlIG1vZGVsIGJhc2VkIG9uIHRoZSBzZW5zaXRpdml0eSAoR2luaSBpbmRleCkNCg0Kb3VyIGZpbmFsIHRyZWUgaXM6DQoNCiFbXShpbWFnZXMvMDAwMDEyLnBuZykNCg0KIyMgQ2x1c3RlcmluZyAqKkFuYWx5c2lzOioqDQoNCkluIHRoaXMgYW5hbHlzaXMsIHdlIGFwcGx5IEstbWVhbnMgY2x1c3RlcmluZyB0byB0aGUgZGF0YXNldCB1c2luZyBkaWZmZXJlbnQgdmFsdWVzIG9mIEsuIEstbWVhbnMgY2x1c3RlcmluZyBpcyBhbiB1bnN1cGVydmlzZWQgbGVhcm5pbmcgYWxnb3JpdGhtIHRoYXQgcGFydGl0aW9ucyB0aGUgZGF0YSBpbnRvIEsgY2x1c3RlcnMgYmFzZWQgb24gc2ltaWxhcml0eS4gV2Ugd2lsbCBleHBsb3JlIHRocmVlIGRpZmZlcmVudCB2YWx1ZXMgb2YgSyBhbmQgZXZhbHVhdGUgdGhlIGNsdXN0ZXJpbmcgcmVzdWx0cyB1c2luZyB2YXJpb3VzIG1ldHJpY3MuDQoNCiMjIyBSZW1vdmluZyB0aGUgY2xhc3MgbGFiZWwgYW5kIHByZXBhcmluZyB0aGUgZGF0YXNldCBmb3IgQ2x1c3RlcmluZw0KDQpgYGB7cn0NCiANCm9yaWdpbmFsX2RhdGEgPC0gUHJlcHJvY2Vzc2VkX2RhdGFzZXQNCg0KIyBSZW1vdmUgYW55IG5vbi1udW1lcmljIGF0dHJpYnV0ZXMNCm51bWVyaWNfZGF0YSA8LSBvcmlnaW5hbF9kYXRhWywgc2FwcGx5KG9yaWdpbmFsX2RhdGEsIGlzLm51bWVyaWMpXQ0KDQojIFJlbW92ZSB0aGUgY2xhc3MgbGFiZWwgJ3dpbGxfZ29fdG9fY29sbGVnZScNCm51bWVyaWNfZGF0YSA8LSBudW1lcmljX2RhdGFbLCAhKG5hbWVzKG51bWVyaWNfZGF0YSkgPT0gJ3dpbGxfZ29fdG9fY29sbGVnZScpXQ0KYGBgDQoNCk5vdywgdGhlICdudW1lcmljX2RhdGFzZXQnIGRhdGFzZXQgY29udGFpbnMgb25seSBudW1lcmljIGF0dHJpYnV0ZXMgd2l0aG91dCB0aGUgY2xhc3MgbGFiZWwsIHdoaWNoIG1ha2VzIGl0IHJlYWR5IGZvciB0aGUgY2x1c3RlcmluZyBwcm9jZXNzLg0KDQpcX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfDQoNCiMjIEs9Mg0KDQpJbiBvdXIgZXhwbG9yYXRpb24gb2Ygay1tZWFucyBjbHVzdGVyaW5nLCB3ZSB3aWxsIHN0YXJ0IGJ5IGV4YW1pbmluZyB0aGUgZGF0YXNldCB3aXRoIEs9MiwgcmVwcmVzZW50aW5nIGFuIGluaXRpYWwgYXR0ZW1wdCB0byBwYXJ0aXRpb24gdGhlIGRhdGEgaW50byB0d28gZGlzdGluY3QgY2x1c3RlcnMuIFRoaXMgaW5pdGlhbCBhbmFseXNpcyB3aWxsIHNlcnZlIGFzIGEgZm91bmRhdGlvbmFsIHN0ZXAgaW4gdW5kZXJzdGFuZGluZyB0aGUgaW5oZXJlbnQgc3RydWN0dXJlIGFuZCBwYXR0ZXJucw0KDQpgYGB7cn0NCiMgay1tZWFucyBjbHVzdGVyaW5nIHNldCBhIHNlZWQgZm9yIHJhbmRvbSBudW1iZXIgZ2VuZXJhdGlvbiB0byBtYWtlIHRoZSByZXN1bHRzIHJlcHJvZHVjaWJsZSANCnNldC5zZWVkKDg5NTMpDQoNCiMgcnVuIGttZWFucyBjbHVzdGVyaW5nIHRvIGZpbmQgMiBjbHVzdGVycw0Ka21lYW5zLnJlc3VsdCA8LSBrbWVhbnMobnVtZXJpY19kYXRhLCAyKQ0KDQojIHZpc3VhbGl6ZSBjbHVzdGVyaW5nDQpsaWJyYXJ5KGZhY3RvZXh0cmEpDQpmdml6X2NsdXN0ZXIoa21lYW5zLnJlc3VsdCwgZGF0YSA9IG51bWVyaWNfZGF0YSkNCg0KIyBwcmludCB0aGUgY2x1c3RlcmluZyByZXN1bHQNCnByaW50KGttZWFucy5yZXN1bHQpDQoNCg0KYGBgDQoNCiMjIyMgVGhlIFNpbGhvdWV0dGUgY29lZmZpY2llbnQNCg0KYGBge3J9DQojYXZlcmFnZSBmb3IgZWFjaCBjbHVzdGVyIA0KYXZnX3NpbCA8LSBzaWxob3VldHRlKGttZWFucy5yZXN1bHQkY2x1c3RlciwgZGlzdChudW1lcmljX2RhdGEpKSANCg0KI2stbWVhbnMgY2x1c3RlcmluZyB3aXRoIGVzdGltYXRpbmcgayBhbmQgaW5pdGlhbGl6YXRpb25zIA0KZnZpel9zaWxob3VldHRlKGF2Z19zaWwpDQoNCmBgYA0KDQojIyMjIFRoZSB0b3RhbCB3aXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlcw0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRvdGFsIHdpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmVzDQp0b3RhbF93aXRoaW5zcyA8LSBrbWVhbnMucmVzdWx0JHRvdC53aXRoaW5zcw0KY2F0KCJUb3RhbCBXaXRoaW4tQ2x1c3RlciBTdW0gb2YgU3F1YXJlczoiLCBzdW0odG90YWxfd2l0aGluc3MpLCAiXG4iKQ0KDQp0cnVlX2xhYmVscyA8LSBjKDEsIDEsIDIsIDEsIDIsIDIsIDMsIDMsIDQsIDQpICANCg0KY2x1c3Rlcl9hc3NpZ25tZW50cyA8LSBrbWVhbnMucmVzdWx0JGNsdXN0ZXINCiANCmBgYA0KDQojIyMjIEJDdWJlZCByZWNhbGwgYW5kIHByZWNpc2lvbg0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIEJDdWJlZCBwcmVjaXNpb24NCnByZWNpc2lvbiA8LSAwDQpmb3IgKGkgaW4gdW5pcXVlKHRydWVfbGFiZWxzKSkgew0KICBjbHVzdGVyX2luZGljZXMgPC0gd2hpY2godHJ1ZV9sYWJlbHMgPT0gaSkNCiAgcHJlY2lzaW9uIDwtIHByZWNpc2lvbiArIHN1bSgodGFibGUoY2x1c3Rlcl9hc3NpZ25tZW50c1tjbHVzdGVyX2luZGljZXNdKSAqICh0YWJsZShjbHVzdGVyX2Fzc2lnbm1lbnRzW2NsdXN0ZXJfaW5kaWNlc10pIC0gMSkpIC8gc3VtKHRhYmxlKGNsdXN0ZXJfYXNzaWdubWVudHNbY2x1c3Rlcl9pbmRpY2VzXSkpKQ0KfQ0KcHJlY2lzaW9uIDwtIHByZWNpc2lvbiAvIHN1bSh0YWJsZShjbHVzdGVyX2Fzc2lnbm1lbnRzKSkNCg0KIyBDYWxjdWxhdGUgQkN1YmVkIHJlY2FsbA0KcmVjYWxsIDwtIDANCmZvciAoaiBpbiB1bmlxdWUoY2x1c3Rlcl9hc3NpZ25tZW50cykpIHsNCiAgY2x1c3Rlcl9pbmRpY2VzIDwtIHdoaWNoKGNsdXN0ZXJfYXNzaWdubWVudHMgPT0gaikNCiAgcmVjYWxsIDwtIHJlY2FsbCArIHN1bSgodGFibGUodHJ1ZV9sYWJlbHNbY2x1c3Rlcl9pbmRpY2VzXSkgKiAodGFibGUodHJ1ZV9sYWJlbHNbY2x1c3Rlcl9pbmRpY2VzXSkgLSAxKSkgLyBzdW0odGFibGUodHJ1ZV9sYWJlbHNbY2x1c3Rlcl9pbmRpY2VzXSkpKQ0KfQ0KcmVjYWxsIDwtIHJlY2FsbCAvIHN1bSh0YWJsZSh0cnVlX2xhYmVscykpDQoNCmNhdCgiQkN1YmVkIFByZWNpc2lvbjoiLCBwcmVjaXNpb24sICJcbiIpDQpjYXQoIkJDdWJlZCBSZWNhbGw6IiwgcmVjYWxsLCAiXG4iKQ0KDQoNCmBgYA0KDQojIyMgKipDbHVzdGVyIEFuYWx5c2lzIGZvciBLPTI6KioNCg0KLSAgICoqU2lsaG91ZXR0ZSBXaWR0aDoqKiAwLjM2DQoNCiAgICAtICAgSW5kaWNhdGVzIHJlbGF0aXZlbHkgd2VsbC1zZXBhcmF0ZWQgYW5kIGRpc3RpbmN0IGNsdXN0ZXJzLg0KDQotICAgKipXaXRoaW4tQ2x1c3RlciBTdW0gb2YgU3F1YXJlczoqKiA4NjY5LjA1OQ0KDQogICAgLSAgIE1vZGVyYXRlIGNvbXBhY3RuZXNzOyBjbHVzdGVycyBhcmUgbm90IHRvbyB0aWdodC4NCg0KLSAgICoqQkN1YmVkIFByZWNpc2lvbjoqKiAwLjAwMjMNCg0KICAgIC0gICBMb3cgcHJlY2lzaW9uIHN1Z2dlc3RzIHBvdGVudGlhbCBtaXNhc3NpZ25tZW50cy4NCg0KLSAgICoqQkN1YmVkIFJlY2FsbDoqKiAwLjExNjcNCg0KICAgIC0gICBMb3cgcmVjYWxsIGluZGljYXRlcyBpbmNvbXBsZXRlIHJlcHJlc2VudGF0aW9uIG9mIHRydWUgY2x1c3RlcnMuDQoNClNvLCB0aGUgY2x1c3RlcnMgYXJlIG1vZGVyYXRlbHkgY29tcGFjdCB3aXRoIGEgcmVhc29uYWJsZSBzaWxob3VldHRlIHdpZHRoLCBidXQgbG93IHByZWNpc2lvbiBhbmQgcmVjYWxsIHN1Z2dlc3QgcG90ZW50aWFsIG1pc2NsYXNzaWZpY2F0aW9uIGlzc3Vlcy4NCg0KXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cXw0KDQojIyBLPTUNCg0KTW92aW5nIHRvIEs9NSwgb3VyIGV4cGxvcmF0aW9uIGluIGstbWVhbnMgY2x1c3RlcmluZyBhZHZhbmNlcyB0byBhIGNvbmZpZ3VyYXRpb24gd2l0aCBmaXZlIGNsdXN0ZXJzLiBUaGlzIGFkanVzdG1lbnQgYWxsb3dzIGZvciBhIG1vcmUgZGV0YWlsZWQgcGFydGl0aW9uaW5nIG9mIHRoZSBkYXRhc2V0LCBwb3RlbnRpYWxseSByZXZlYWxpbmcgZmluZXIgZ3JhaW5lZCBwYXR0ZXJucyBhbmQgZGlzdGluY3Rpb25zIGFtb25nIHRoZSBkYXRhIHBvaW50cy4gVGhlIGFuYWx5c2lzIGF0IEs9NSB3aWxsIG9mZmVyIGluc2lnaHRzIGludG8gdGhlIG5hdHVyZSBvZiB0aGVzZSBjbHVzdGVycywgYXNzZXNzaW5nIHRoZWlyIGNoYXJhY3RlcmlzdGljcywgY29tcGFjdG5lc3MsIGFuZCBzZXBhcmF0aW9uLCBhcyB3ZSBzdHJpdmUgdG8gb3B0aW1pemUgdGhlIGNsdXN0ZXJpbmcgY29uZmlndXJhdGlvbiBmb3IgdGhlIHNwZWNpZmljIG51YW5jZXMgb2Ygb3VyIGRhdGFzZXQuDQoNCmBgYHtyfQ0KIyBrLW1lYW5zIGNsdXN0ZXJpbmcgc2V0IGEgc2VlZCBmb3IgcmFuZG9tIG51bWJlciBnZW5lcmF0aW9uIHRvIG1ha2UgdGhlIHJlc3VsdHMgcmVwcm9kdWNpYmxlIA0Kc2V0LnNlZWQoODk1MykNCg0KIyBydW4ga21lYW5zIGNsdXN0ZXJpbmcgdG8gZmluZCA0IGNsdXN0ZXJzDQprbWVhbnMucmVzdWx0IDwtIGttZWFucyhudW1lcmljX2RhdGEsIDUpDQoNCiMgdmlzdWFsaXplIGNsdXN0ZXJpbmcNCmxpYnJhcnkoZmFjdG9leHRyYSkNCmZ2aXpfY2x1c3RlcihrbWVhbnMucmVzdWx0LCBkYXRhID0gbnVtZXJpY19kYXRhKQ0KDQojIHByaW50IHRoZSBjbHVzdGVyaW5nIHJlc3VsdA0KcHJpbnQoa21lYW5zLnJlc3VsdCkNCg0KDQpgYGANCg0KIyMjIyBUaGUgU2lsaG91ZXR0ZSBjb2VmZmljaWVudA0KDQpgYGB7cn0NCiNhdmVyYWdlIGZvciBlYWNoIGNsdXN0ZXIgDQphdmdfc2lsIDwtIHNpbGhvdWV0dGUoa21lYW5zLnJlc3VsdCRjbHVzdGVyLCBkaXN0KG51bWVyaWNfZGF0YSkpIA0KDQojay1tZWFucyBjbHVzdGVyaW5nIHdpdGggZXN0aW1hdGluZyBrIGFuZCBpbml0aWFsaXphdGlvbnMgDQpmdml6X3NpbGhvdWV0dGUoYXZnX3NpbCkNCg0KYGBgDQoNCiMjIyMgVGhlIHRvdGFsIHdpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmVzDQoNCmBgYHtyfQ0KIyBDYWxjdWxhdGUgdG90YWwgd2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZXMNCnRvdGFsX3dpdGhpbnNzIDwtIGttZWFucy5yZXN1bHQkdG90LndpdGhpbnNzDQpjYXQoIlRvdGFsIFdpdGhpbi1DbHVzdGVyIFN1bSBvZiBTcXVhcmVzOiIsIHN1bSh0b3RhbF93aXRoaW5zcyksICJcbiIpDQoNCnRydWVfbGFiZWxzIDwtIGMoMSwgMSwgMiwgMSwgMiwgMiwgMywgMywgNCwgNCkgIA0KDQpjbHVzdGVyX2Fzc2lnbm1lbnRzIDwtIGttZWFucy5yZXN1bHQkY2x1c3Rlcg0KIA0KYGBgDQoNCiMjIyMgQkN1YmVkIHJlY2FsbCBhbmQgcHJlY2lzaW9uDQoNCmBgYHtyfQ0KIyBDYWxjdWxhdGUgQkN1YmVkIHByZWNpc2lvbg0KcHJlY2lzaW9uIDwtIDANCmZvciAoaSBpbiB1bmlxdWUodHJ1ZV9sYWJlbHMpKSB7DQogIGNsdXN0ZXJfaW5kaWNlcyA8LSB3aGljaCh0cnVlX2xhYmVscyA9PSBpKQ0KICBwcmVjaXNpb24gPC0gcHJlY2lzaW9uICsgc3VtKCh0YWJsZShjbHVzdGVyX2Fzc2lnbm1lbnRzW2NsdXN0ZXJfaW5kaWNlc10pICogKHRhYmxlKGNsdXN0ZXJfYXNzaWdubWVudHNbY2x1c3Rlcl9pbmRpY2VzXSkgLSAxKSkgLyBzdW0odGFibGUoY2x1c3Rlcl9hc3NpZ25tZW50c1tjbHVzdGVyX2luZGljZXNdKSkpDQp9DQpwcmVjaXNpb24gPC0gcHJlY2lzaW9uIC8gc3VtKHRhYmxlKGNsdXN0ZXJfYXNzaWdubWVudHMpKQ0KDQojIENhbGN1bGF0ZSBCQ3ViZWQgcmVjYWxsDQpyZWNhbGwgPC0gMA0KZm9yIChqIGluIHVuaXF1ZShjbHVzdGVyX2Fzc2lnbm1lbnRzKSkgew0KICBjbHVzdGVyX2luZGljZXMgPC0gd2hpY2goY2x1c3Rlcl9hc3NpZ25tZW50cyA9PSBqKQ0KICByZWNhbGwgPC0gcmVjYWxsICsgc3VtKCh0YWJsZSh0cnVlX2xhYmVsc1tjbHVzdGVyX2luZGljZXNdKSAqICh0YWJsZSh0cnVlX2xhYmVsc1tjbHVzdGVyX2luZGljZXNdKSAtIDEpKSAvIHN1bSh0YWJsZSh0cnVlX2xhYmVsc1tjbHVzdGVyX2luZGljZXNdKSkpDQp9DQpyZWNhbGwgPC0gcmVjYWxsIC8gc3VtKHRhYmxlKHRydWVfbGFiZWxzKSkNCg0KY2F0KCJCQ3ViZWQgUHJlY2lzaW9uOiIsIHByZWNpc2lvbiwgIlxuIikNCmNhdCgiQkN1YmVkIFJlY2FsbDoiLCByZWNhbGwsICJcbiIpDQoNCg0KYGBgDQoNCiMjIyAqKkNsdXN0ZXIgQW5hbHlzaXMgZm9yIEs9NToqKg0KDQotICAgKipTaWxob3VldHRlIFdpZHRoOioqIDAuMjkNCg0KICAgIC0gICBJbmRpY2F0ZXMgbGVzcyB3ZWxsLXNlcGFyYXRlZCBjbHVzdGVycyBjb21wYXJlZCB0byBrPTINCg0KLSAgICoqV2l0aGluLUNsdXN0ZXIgU3VtIG9mIFNxdWFyZXM6KiogNDMwMy44NDENCg0KICAgIC0gICBMb3dlciB3aXRoaW4tY2x1c3RlciBzdW0gc3VnZ2VzdHMgbW9yZSBjb21wYWN0IGNsdXN0ZXJzIHRoYW4gaz0yDQoNCi0gICAqKkJDdWJlZCBQcmVjaXNpb246KiogMC4wMDE2Ng0KDQogICAgLSAgIExvdyBwcmVjaXNpb24gaW1wbGllcyBwb3RlbnRpYWwgbWlzY2xhc3NpZmljYXRpb25zLg0KDQotICAgKipCQ3ViZWQgUmVjYWxsOioqIDAuMTMzMw0KDQogICAgLSAgIFNsaWdodGx5IGhpZ2hlciByZWNhbGwgY29tcGFyZWQgdG8gaz0yLCBidXQgc3RpbGwgbG93Lg0KDQoqKkZpbmFsIEFuYWx5c2lzOioqIENsdXN0ZXJzIGFyZSBtb3JlIGNvbXBhY3QgdGhhbiBrPTIgYnV0IGxlc3Mgd2VsbC1zZXBhcmF0ZWQuIFByZWNpc2lvbiBhbmQgcmVjYWxsIHJlbWFpbiBsb3csIGluZGljYXRpbmcgcG90ZW50aWFsIG1pc2NsYXNzaWZpY2F0aW9ucy4NCg0KXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cXw0KDQojIyBLPTcNCg0KUHJvY2VlZGluZyB0byBrPTcsIG91ciBpbnZlc3RpZ2F0aW9uIGluIGstbWVhbnMgY2x1c3RlcmluZyBleHBhbmRzIGZ1cnRoZXIgYXMgd2UgZXhwbG9yZSBhIGNvbmZpZ3VyYXRpb24gd2l0aCBzZXZlbiBjbHVzdGVycy4gVGhpcyBhZGp1c3RtZW50IGFpbXMgdG8gY2FwdHVyZSBldmVuIG1vcmUgbnVhbmNlZCBwYXR0ZXJucyBhbmQgdmFyaWF0aW9ucyB3aXRoaW4gdGhlIGRhdGFzZXQuIEFuYWx5emluZyB0aGUgY2hhcmFjdGVyaXN0aWNzIG9mIHRoZSBzZXZlbiBjbHVzdGVycyB3aWxsIHByb3ZpZGUgYSBtb3JlIGdyYW51bGFyIHVuZGVyc3RhbmRpbmcgb2YgdGhlIHVuZGVybHlpbmcgc3RydWN0dXJlLCBwb3RlbnRpYWxseSByZXZlYWxpbmcgc3VidGxldGllcyB0aGF0IG1pZ2h0IG5vdCBiZSBhcyBldmlkZW50IHdpdGggZmV3ZXIgY2x1c3RlcnMuIEFzIHdlIGRlbHZlIGludG8gaz03LCBvdXIgb2JqZWN0aXZlIGlzIHRvIHJlZmluZSBvdXIgY2x1c3RlcmluZyBjb25maWd1cmF0aW9uIHRvIGFsaWduIG1vcmUgY2xvc2VseSB3aXRoIHRoZSBpbnRyaWNhdGUgZGV0YWlscyBwcmVzZW50IGluIHRoZSBkYXRhc2V0Lg0KDQpgYGB7cn0NCiMgay1tZWFucyBjbHVzdGVyaW5nIHNldCBhIHNlZWQgZm9yIHJhbmRvbSBudW1iZXIgZ2VuZXJhdGlvbiB0byBtYWtlIHRoZSByZXN1bHRzIHJlcHJvZHVjaWJsZSANCnNldC5zZWVkKDg5NTMpDQoNCiMgcnVuIGttZWFucyBjbHVzdGVyaW5nIHRvIGZpbmQgMiBjbHVzdGVycw0Ka21lYW5zLnJlc3VsdCA8LSBrbWVhbnMobnVtZXJpY19kYXRhLCA3KQ0KDQojIHZpc3VhbGl6ZSBjbHVzdGVyaW5nDQpsaWJyYXJ5KGZhY3RvZXh0cmEpDQpmdml6X2NsdXN0ZXIoa21lYW5zLnJlc3VsdCwgZGF0YSA9IG51bWVyaWNfZGF0YSkNCg0KIyBwcmludCB0aGUgY2x1c3RlcmluZyByZXN1bHQNCnByaW50KGttZWFucy5yZXN1bHQpDQoNCg0KYGBgDQoNCiMjIyMgVGhlIFNpbGhvdWV0dGUgY29lZmZpY2llbnQNCg0KYGBge3J9DQojYXZlcmFnZSBmb3IgZWFjaCBjbHVzdGVyIA0KYXZnX3NpbCA8LSBzaWxob3VldHRlKGttZWFucy5yZXN1bHQkY2x1c3RlciwgZGlzdChudW1lcmljX2RhdGEpKSANCg0KI2stbWVhbnMgY2x1c3RlcmluZyB3aXRoIGVzdGltYXRpbmcgayBhbmQgaW5pdGlhbGl6YXRpb25zIA0KZnZpel9zaWxob3VldHRlKGF2Z19zaWwpDQoNCmBgYA0KDQojIyMjIFRoZSB0b3RhbCB3aXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlcw0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRvdGFsIHdpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmVzDQp0b3RhbF93aXRoaW5zcyA8LSBrbWVhbnMucmVzdWx0JHRvdC53aXRoaW5zcw0KY2F0KCJUb3RhbCBXaXRoaW4tQ2x1c3RlciBTdW0gb2YgU3F1YXJlczoiLCBzdW0odG90YWxfd2l0aGluc3MpLCAiXG4iKQ0KDQp0cnVlX2xhYmVscyA8LSBjKDEsIDEsIDIsIDEsIDIsIDIsIDMsIDMsIDQsIDQpICANCg0KY2x1c3Rlcl9hc3NpZ25tZW50cyA8LSBrbWVhbnMucmVzdWx0JGNsdXN0ZXINCiANCmBgYA0KDQojIyMjIEJDdWJlZCByZWNhbGwgYW5kIHByZWNpc2lvbg0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIEJDdWJlZCBwcmVjaXNpb24NCnByZWNpc2lvbiA8LSAwDQpmb3IgKGkgaW4gdW5pcXVlKHRydWVfbGFiZWxzKSkgew0KICBjbHVzdGVyX2luZGljZXMgPC0gd2hpY2godHJ1ZV9sYWJlbHMgPT0gaSkNCiAgcHJlY2lzaW9uIDwtIHByZWNpc2lvbiArIHN1bSgodGFibGUoY2x1c3Rlcl9hc3NpZ25tZW50c1tjbHVzdGVyX2luZGljZXNdKSAqICh0YWJsZShjbHVzdGVyX2Fzc2lnbm1lbnRzW2NsdXN0ZXJfaW5kaWNlc10pIC0gMSkpIC8gc3VtKHRhYmxlKGNsdXN0ZXJfYXNzaWdubWVudHNbY2x1c3Rlcl9pbmRpY2VzXSkpKQ0KfQ0KcHJlY2lzaW9uIDwtIHByZWNpc2lvbiAvIHN1bSh0YWJsZShjbHVzdGVyX2Fzc2lnbm1lbnRzKSkNCg0KIyBDYWxjdWxhdGUgQkN1YmVkIHJlY2FsbA0KcmVjYWxsIDwtIDANCmZvciAoaiBpbiB1bmlxdWUoY2x1c3Rlcl9hc3NpZ25tZW50cykpIHsNCiAgY2x1c3Rlcl9pbmRpY2VzIDwtIHdoaWNoKGNsdXN0ZXJfYXNzaWdubWVudHMgPT0gaikNCiAgcmVjYWxsIDwtIHJlY2FsbCArIHN1bSgodGFibGUodHJ1ZV9sYWJlbHNbY2x1c3Rlcl9pbmRpY2VzXSkgKiAodGFibGUodHJ1ZV9sYWJlbHNbY2x1c3Rlcl9pbmRpY2VzXSkgLSAxKSkgLyBzdW0odGFibGUodHJ1ZV9sYWJlbHNbY2x1c3Rlcl9pbmRpY2VzXSkpKQ0KfQ0KcmVjYWxsIDwtIHJlY2FsbCAvIHN1bSh0YWJsZSh0cnVlX2xhYmVscykpDQoNCmNhdCgiQkN1YmVkIFByZWNpc2lvbjoiLCBwcmVjaXNpb24sICJcbiIpDQpjYXQoIkJDdWJlZCBSZWNhbGw6IiwgcmVjYWxsLCAiXG4iKQ0KDQoNCmBgYA0KDQojIyMgKipDbHVzdGVyIEFuYWx5c2lzIGZvcioqIGs9NzoNCg0KLSAgICoqU2lsaG91ZXR0ZSBXaWR0aDoqKiAwLjI3DQoNCiAgICAtICAgSW5kaWNhdGVzIGNsdXN0ZXJzIGFyZSBsZXNzIHdlbGwtc2VwYXJhdGVkIGNvbXBhcmVkIHRvIGs9MiBhbmQgaz01DQoNCi0gICAqKldpdGhpbi1DbHVzdGVyIFN1bSBvZiBTcXVhcmVzOioqIDMzODkuNjU2DQoNCiAgICAtICAgTG93ZXIgd2l0aGluLWNsdXN0ZXIgc3VtIHN1Z2dlc3RzIG1vcmUgY29tcGFjdCBjbHVzdGVycyB0aGFuIGs9NQ0KDQotICAgKipCQ3ViZWQgUHJlY2lzaW9uOioqIDAuMDAwNjcNCg0KICAgIC0gICBWZXJ5IGxvdyBwcmVjaXNpb24gaW5kaWNhdGluZyBzaWduaWZpY2FudCBtaXNjbGFzc2lmaWNhdGlvbnMuDQoNCi0gICAqKkJDdWJlZCBSZWNhbGw6KiogMC4wNjcNCg0KICAgIC0gICBMb3cgcmVjYWxsIHN1Z2dlc3RpbmcgaW5jb21wbGV0ZSByZXByZXNlbnRhdGlvbiBvZiB0cnVlIGNsdXN0ZXJzLg0KDQoqKkZpbmFsIEFuYWx5c2lzOioqIENsdXN0ZXJzIGFyZSBsZXNzIHdlbGwtc2VwYXJhdGVkIHRoYW4gaz0yIGFuZCBrPTUgYW5kIHdoaWxlIG1vcmUgY29tcGFjdCB0aGFuIGs9NSwgcHJlY2lzaW9uIGFuZCByZWNhbGwgcmVtYWluIGxvdywgaW5kaWNhdGluZyBwb3RlbnRpYWwgbWlzY2xhc3NpZmljYXRpb25zLg0KDQpcX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfDQoNCiMjIyAqKkVsYm93IE1ldGhvZCBJbnNpZ2h0OioqDQoNCldlIGltcGxlbWVudGVkIHRoZSBFbGJvdyBNZXRob2QgZm9yIGRldGVybWluaW5nIHRoZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycyAoaykgaW4gYSBrLW1lYW5zIGNsdXN0ZXJpbmcgYWxnb3JpdGhtLg0KDQpgYGB7cn0NCiMgRnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRvdGFsIHdpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmVzICh3c3MpDQp3c3MgPC0gZnVuY3Rpb24oaykgew0KICBrbWVhbnNfcmVzdWx0IDwtIGttZWFucyhudW1lcmljX2RhdGEsIGNlbnRlcnMgPSBrLCBuc3RhcnQgPSAxMCkgICMgWW91IGNhbiBhZGp1c3QgbnN0YXJ0IGJhc2VkIG9uIHlvdXIgcHJlZmVyZW5jZQ0KICByZXR1cm4oc3VtKGttZWFuc19yZXN1bHQkdG90LndpdGhpbnNzKSkNCn0NCg0KIyBDYWxjdWxhdGUgdGhlIHRvdGFsIHdpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmVzIGZvciBkaWZmZXJlbnQgdmFsdWVzIG9mIGsNCmtfdmFsdWVzIDwtIDE6MTAgICMgWW91IGNhbiBhZGp1c3QgdGhlIHJhbmdlIG9mIGsgdmFsdWVzDQp3c3NfdmFsdWVzIDwtIHNhcHBseShrX3ZhbHVlcywgd3NzKQ0KDQojIFBsb3QgdGhlIGVsYm93IGN1cnZlDQpwbG90KGtfdmFsdWVzLCB3c3NfdmFsdWVzLCB0eXBlID0gImIiLCBwY2ggPSAxOSwgZnJhbWUgPSBGQUxTRSwgDQogICAgIHhsYWIgPSAiTnVtYmVyIG9mIENsdXN0ZXJzIChrKSIsIHlsYWIgPSAiVG90YWwgV2l0aGluLUNsdXN0ZXIgU3VtIG9mIFNxdWFyZXMgKFdTUykiLA0KICAgICBtYWluID0gIkVsYm93IE1ldGhvZCIpDQoNCg0KYGBgDQoNClRoZSBlbGJvdyBtZXRob2QgaGFzIGluZGljYXRlZCB0aGF0IEs9NSBpcyBhIHJlYXNvbmFibGUgY2hvaWNlIGluIHRlcm1zIG9mIGJhbGFuY2luZyB0aGUgdHJhZGUtb2ZmIGJldHdlZW4gY2FwdHVyaW5nIHZhcmlhbmNlIGFuZCBub3Qgb3Zlcmx5IGNvbXBsaWNhdGluZyB0aGUgbW9kZWwgd2l0aCB0b28gbWFueSBjbHVzdGVycy4NCg0KIyMgRmluZGluZ3MNCg0KfCAgICAgICAgICAgICB8ICAgICAgICB8IDUwJS01MCUgfCAgICAgICAgfCAgICAgICAgIHwgNzAlLTMwJSB8ICAgICAgICB8ICAgICAgICAgfCA4MCUtMjAlIHwgICAgICAgIHwNCnwtLS0tLS0tLXwtLS0tLS0tLXwtLS0tLS0tLXwtLS0tLS0tLXwtLS0tLS0tLXwtLS0tLS0tLXwtLS0tLS0tLXwtLS0tLS0tLXwtLS0tLS0tLXwtLS0tLS0tLXwNCnwgICAgICAgICAgICAgfCBpZyAgICAgfCBpZ3IgICAgIHwgZ2luaSAgIHwgaWcgICAgICB8IGlnciAgICAgfCBnaW5pICAgfCBpZyAgICAgIHwgaWdyICAgICB8IGdpbmkgICB8DQp8IEFjY3VyYWN5ICAgIHwgODQuMjIlIHwgODUuMjElICB8IDg1LjIxJSB8IDgyLjM3ICUgfCA4MC4zNCUgIHwgNzkuNjYlIHwgODMuODQgJSB8IDgwLjAzJSAgfCA4MC44MSUgfA0KfCBFcnJvciByYXRlICB8IDE1Ljc4JSB8IDE0Ljc5JSAgfCAxNC43OSUgfCAxNy42MyUgIHwgMTkuNjYlICB8IDIwLjM0JSB8IDE2LjE2JSAgfCAxOS45NyUgIHwgMTkuMTklIHwNCnwgU2Vuc2l0aXZpdHkgfCA4OS41MyUgfCA4Ny42MCUgIHwgODguMzclIHwgNzUuODklICB8IDc3LjMwJSAgfCA3OC4wMSUgfCA4Ni43MyUgIHwgODQuNjklICB8IDc2LjUzJSB8DQp8IFNwZWNpZmljaXR5IHwgNzguNzElIHwgODIuNzMlICB8IDgxLjkzJSB8IDg4LjMxJSAgfCA4My4xMiUgIHwgODEuMTclIHwgODEuMDAlICB8IDc2LjAwJSAgfCA4NS4wMCUgfA0KfCBQcmVjaXNpb24gICB8IDgxLjM0JSB8IDg0LjAxJSAgfCA4My41MiUgfCA4NS42MCUgIHwgODAuNzQlICB8IDc5LjE0JSB8IDgxLjczJSAgfCA3Ny41NyUgIHwgODMuMzMlIHwNCg0KQWNjdXJhY3kgKHJlY29nbml0aW9uIHJhdGUpOiBQZXJjZW50YWdlIG9mIHRlc3Qgc2V0IHR1cGxlcyB0aGF0IGFyZSBjb3JyZWN0bHkgY2xhc3NpZmllZA0KDQpFcnJvciByYXRlIChtaXNjbGFzc2lmaWNhdGlvbiByYXRlKTogMSAtLSBhY2N1cmFjeQ0KDQpTZW5zaXRpdml0eSAocmVjYWxsKTogVHJ1ZSBwb3NpdGl2ZSByZWNvZ25pdGlvbiByYXRlDQoNClNwZWNpZmljaXR5OiBUcnVlIG5lZ2F0aXZlIHJlY29nbml0aW9uIHJhdGUNCg0KUHJlY2lzaW9uIChleGFjdG5lc3MpOiBXaGF0ICUgb2YgdHVwbGVzIGxhYmVsZWQgYXMgcG9zaXRpdmUgYXJlIGFjdHVhbGx5IHBvc2l0aXZlLg0KDQpGb3IgcGFydGl0aW9uIDUwJS01MCUgOiBnYWluIHJhdGlvIGFuZCBnaW5pIGluZGV4IGVxdWFscyBpbiBhY2N1cmFjeSA4NS4yMSUgYnV0IGFzIHdlIG1lbnRpb24gYmVmb3JlIGluIFtiZXN0IG1vZGVsXSB3ZSBjaG9vc2UgYmFzZWQgb24gU2Vuc2l0aXZpdHksIGdpbmkgaW5kZXggaXMgdGhlIGJlc3QgbW9kZWwgd2l0aCA4OC4zNyUgc2Vuc2l0aXZpdHkuDQoNCkZvciBwYXJ0aXRpb24gNzAlLTMwJTogaW5mb3JtYXRpb24gZ2FpbiBpcyB0aGUgYmVzdCBtb2RlbCBmb3IgdGhpcyBwYXJ0aXRpb24gd2l0aCBhY2N1cmFjeSA4Mi4zNyAlDQoNCkZvciBwYXJ0aXRpb24gODAlLTIwJTogaW5mb3JtYXRpb24gZ2FpbiBpcyB0aGUgYmVzdCBtb2RlbCBmb3IgdGhpcyBwYXJ0aXRpb24gd2l0aCBhY2N1cmFjeSA4My44NCAlDQoNCm92ZXJhbGwgNTAlLTUwJSB3YXNhIHRoZSBiZXN0IHBhcnRpdGlvbiB3aXRoIGFsZ29yaXRobSBnaW5pIGluZGV4IHdpdGggYWNjdXJhY3kgODUuMjElLg0KDQojIyBGaW5kaW5ncw0KDQojIyMgQ2xhc3NpZmljYXRpb246DQoNCnwgICAgICAgICAgICAgfCAgICAgICAgfCA1MCUtNTAlIHwgICAgICAgIHwgICAgICAgICB8IDcwJS0zMCUgfCAgICAgICAgfCAgICAgICAgIHwgODAlLTIwJSB8ICAgICAgICB8DQp8LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18DQp8ICAgICAgICAgICAgIHwgaWcgICAgIHwgaWdyICAgICB8IGdpbmkgICB8IGlnICAgICAgfCBpZ3IgICAgIHwgZ2luaSAgIHwgaWcgICAgICB8IGlnciAgICAgfCBnaW5pICAgfA0KfCBBY2N1cmFjeSAgICB8IDg0LjIyJSB8IDg1LjIxJSAgfCA4NS4yMSUgfCA4Mi4zNyAlIHwgODAuMzQlICB8IDc5LjY2JSB8IDgzLjg0ICUgfCA4MC4wMyUgIHwgODAuODElIHwNCnwgRXJyb3IgcmF0ZSAgfCAxNS43OCUgfCAxNC43OSUgIHwgMTQuNzklIHwgMTcuNjMlICB8IDE5LjY2JSAgfCAyMC4zNCUgfCAxNi4xNiUgIHwgMTkuOTclICB8IDE5LjE5JSB8DQp8IFNlbnNpdGl2aXR5IHwgODkuNTMlIHwgODcuNjAlICB8IDg4LjM3JSB8IDc1Ljg5JSAgfCA3Ny4zMCUgIHwgNzguMDElIHwgODYuNzMlICB8IDg0LjY5JSAgfCA3Ni41MyUgfA0KfCBTcGVjaWZpY2l0eSB8IDc4LjcxJSB8IDgyLjczJSAgfCA4MS45MyUgfCA4OC4zMSUgIHwgODMuMTIlICB8IDgxLjE3JSB8IDgxLjAwJSAgfCA3Ni4wMCUgIHwgODUuMDAlIHwNCnwgUHJlY2lzaW9uICAgfCA4MS4zNCUgfCA4NC4wMSUgIHwgODMuNTIlIHwgODUuNjAlICB8IDgwLjc0JSAgfCA3OS4xNCUgfCA4MS43MyUgIHwgNzcuNTclICB8IDgzLjMzJSB8DQoNCkFjY3VyYWN5IChyZWNvZ25pdGlvbiByYXRlKTogUGVyY2VudGFnZSBvZiB0ZXN0IHNldCB0dXBsZXMgdGhhdCBhcmUgY29ycmVjdGx5IGNsYXNzaWZpZWQNCg0KRXJyb3IgcmF0ZSAobWlzY2xhc3NpZmljYXRpb24gcmF0ZSk6IDEgLS0gYWNjdXJhY3kNCg0KU2Vuc2l0aXZpdHkgKHJlY2FsbCk6IFRydWUgcG9zaXRpdmUgcmVjb2duaXRpb24gcmF0ZQ0KDQpTcGVjaWZpY2l0eTogVHJ1ZSBuZWdhdGl2ZSByZWNvZ25pdGlvbiByYXRlDQoNClByZWNpc2lvbiAoZXhhY3RuZXNzKTogV2hhdCAlIG9mIHR1cGxlcyBsYWJlbGVkIGFzIHBvc2l0aXZlIGFyZSBhY3R1YWxseSBwb3NpdGl2ZS4NCg0KRm9yIHBhcnRpdGlvbiA1MCUtNTAlIDogZ2FpbiByYXRpbyBhbmQgZ2luaSBpbmRleCBlcXVhbHMgaW4gYWNjdXJhY3kgODUuMjElIGJ1dCBhcyB3ZSBtZW50aW9uIGJlZm9yZSBpbiBbYmVzdCBtb2RlbF0gd2UgY2hvb3NlIGJhc2VkIG9uIFNlbnNpdGl2aXR5LCBnaW5pIGluZGV4IGlzIHRoZSBiZXN0IG1vZGVsIHdpdGggODguMzclIHNlbnNpdGl2aXR5Lg0KDQpGb3IgcGFydGl0aW9uIDcwJS0zMCU6IGluZm9ybWF0aW9uIGdhaW4gaXMgdGhlIGJlc3QgbW9kZWwgZm9yIHRoaXMgcGFydGl0aW9uIHdpdGggYWNjdXJhY3kgODIuMzcgJQ0KDQpGb3IgcGFydGl0aW9uIDgwJS0yMCU6IGluZm9ybWF0aW9uIGdhaW4gaXMgdGhlIGJlc3QgbW9kZWwgZm9yIHRoaXMgcGFydGl0aW9uIHdpdGggYWNjdXJhY3kgODMuODQgJQ0KDQpvdmVyYWxsIDUwJS01MCUgd2FzYSB0aGUgYmVzdCBwYXJ0aXRpb24gd2l0aCBhbGdvcml0aG0gZ2luaSBpbmRleCB3aXRoIGFjY3VyYWN5IDg1LjIxJS4NCg0KIyMjIENsdXN0ZXJpbmcNCg0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLSsNCnwgTWV0cmljICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBLPTIgICAgICAgICB8IEs9NSAgICAgICAgIHwgSz03ICAgICAgICB8DQorPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSs9PT09PT09PT09PT09Kz09PT09PT09PT09PT0rPT09PT09PT09PT09Kw0KfCBBdmVyYWdlIFNpbGhvdWV0dGUgV2lkdGggICAgICAgICAgICB8IDAuMzYgICAgICAgIHwgMC4yOSAgICAgICAgfCAwLjI3ICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0rDQp8IFRvdGFsIFdpdGhpbi1DbHVzdGVyIFN1bSBvZiBTcXVhcmVzIHwgODY2OS4wNTkgICAgfCA0MzAzLjg0MSAgICB8IDMzODkuNjU2ICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLSsNCnwgQkN1YmVkIFByZWNpc2lvbiAgICAgICAgICAgICAgICAgICAgfCAwLjAwMjMzMzMzMyB8IDAuMDAxNjY2NjY3IHwgMC4wMDA2NyAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tKw0KfCBCQ3ViZWQgUmVjYWxsICAgICAgICAgICAgICAgICAgICAgICB8IDAuMTE2NjY2NyAgIHwgMC4xMzMzMzMzICAgfCAwLjA2NyAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0rDQoNCkluIG91ciBvdmVyYWxsIGFuYWx5c2lzLCB3ZSBldmFsdWF0ZWQgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZSBrLW1lYW5zIGNsdXN0ZXJpbmcgYWxnb3JpdGhtIGZvciB0aHJlZSBkaWZmZXJlbnQgdmFsdWVzIG9mIGsgKDIsIDUsIGFuZCA3KS4gVGhlIGtleSBtZXRyaWNzIHByb3ZpZGUgaW5zaWdodHMgaW50byB0aGUgY2hhcmFjdGVyaXN0aWNzIG9mIHRoZSByZXN1bHRpbmcgY2x1c3RlcnM6DQoNCjEuICAqQXZlcmFnZSBTaWxob3VldHRlIFdpZHRoOioNCg0KICAgIC0gICBUaGUgYXZlcmFnZSBzaWxob3VldHRlIHdpZHRoIG1lYXN1cmVzIHRoZSBzZXBhcmF0aW9uIGFuZCBjb2hlc2lvbiBvZiBjbHVzdGVycy4gQXMgayBpbmNyZWFzZXMgZnJvbSAyIHRvIDcsIHRoZXJlIGlzIGEgZ3JhZHVhbCBkZWNsaW5lIGluIHNpbGhvdWV0dGUgd2lkdGgsIGluZGljYXRpbmcgdGhhdCBjbHVzdGVycyBiZWNvbWUgbGVzcyBkaXN0aW5jdC4gSG93ZXZlciwgaz01IHN0aWxsIG1haW50YWlucyBhIHJlYXNvbmFibGUgc2lsaG91ZXR0ZSB3aWR0aCwgc3VnZ2VzdGluZyBhIGJhbGFuY2UgYmV0d2VlbiBzZXBhcmF0aW9uIGFuZCBjb2hlc2lvbi4NCg0KMi4gICpUb3RhbCBXaXRoaW4tQ2x1c3RlciBTdW0gb2YgU3F1YXJlczoqDQoNCiAgICAtICAgVGhlIHRvdGFsIHdpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmVzIHJlZmxlY3RzIHRoZSBjb21wYWN0bmVzcyBvZiBjbHVzdGVycy4gTm90YWJseSwgaz03IGV4aGliaXRzIHRoZSBsb3dlc3Qgc3VtIG9mIHNxdWFyZXMsIGltcGx5aW5nIG1vcmUgY29tcGFjdCBjbHVzdGVycyBjb21wYXJlZCB0byBrPTUgYW5kIGs9Mi4gVGhpcyBhbGlnbnMgd2l0aCB0aGUgZXhwZWN0YXRpb24gdGhhdCBhIGhpZ2hlciBrIHRlbmRzIHRvIHlpZWxkIG1vcmUgY29tcGFjdCBjbHVzdGVycy4NCg0KMy4gICpCQ3ViZWQgUHJlY2lzaW9uIGFuZCBSZWNhbGw6Kg0KDQogICAgLSAgIEJDdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbCBtZXRyaWNzIGFzc2VzcyB0aGUgYWNjdXJhY3kgYW5kIGNvbXBsZXRlbmVzcyBvZiBjbHVzdGVyaW5nLiBQcmVjaXNpb24gYW5kIHJlY2FsbCB2YWx1ZXMgYXJlIHJlbGF0aXZlbHkgbG93IGFjcm9zcyBhbGwgayB2YWx1ZXMsIGluZGljYXRpbmcgcG90ZW50aWFsIG1pc2NsYXNzaWZpY2F0aW9ucy4gSG93ZXZlciwgaz01IHN0YW5kcyBvdXQgd2l0aCBzbGlnaHRseSBoaWdoZXIgcHJlY2lzaW9uIGFuZCByZWNhbGwgY29tcGFyZWQgdG8gaz0yIGFuZCBrPTcsIHN1Z2dlc3RpbmcgYSBiZXR0ZXIgYmFsYW5jZSBiZXR3ZWVuIGFjY3VyYWN5IGFuZCBjb21wbGV0ZW5lc3MuDQoNCkNvbnNpZGVyaW5nIHRoZSByZXN1bHRzIG9idGFpbmVkIGZyb20gdGhlIGVsYm93IG1ldGhvZCwgd2hpY2ggaWRlbnRpZmllcyBrPTUgYXMgdGhlIG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzLCBvdXIgYW5hbHlzaXMgYWxpZ25zIHdpdGggdGhpcyByZWNvbW1lbmRhdGlvbi4gVGhlIGRldGFpbGVkIGV4YW1pbmF0aW9uIG9mIHNpbGhvdWV0dGUgd2lkdGgsIHdpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmVzLCBhbmQgQkN1YmVkIG1ldHJpY3MgcmVpbmZvcmNlcyB0aGUgY2hvaWNlIG9mIGs9NSBhcyBhIHN1aXRhYmxlIGNvbmZpZ3VyYXRpb24sIHN0cmlraW5nIGEgYmFsYW5jZSBiZXR3ZWVuIGNsdXN0ZXIgZGlzdGluY3RpdmVuZXNzIGFuZCBpbnRlcm5hbCBjb2hlc2lvbi4gVGhpcyBjb21wcmVoZW5zaXZlIGV2YWx1YXRpb24gc3VwcG9ydHMgdGhlIGRlY2lzaW9uIHRvIHByb2NlZWQgd2l0aCBrPTUgZm9yIGEgbW9yZSByZWZpbmVkIGFuZCBlZmZlY3RpdmUgY2x1c3RlcmluZyBzb2x1dGlvbi4NCg0KSW4gdGhlIGZvbGxvd2luZyBmaWd1cmVzLCB0aGUgdmlzdWFsaXNhdGlvbiBvZiB0aGUgY2x1c3RlcnMgZm9yIGVhY2ggdHJpYWw6DQoNCks9MjoNCg0KIVtdKGltYWdlcy9XaGF0c0FwcCBJbWFnZSAyMDIzLTEyLTAyIGF0IDIxLjM3LjMzX2Q2ZTZiNThhLmpwZykNCg0Kaz01DQoNCiFbXShpbWFnZXMvV2hhdHNBcHAgSW1hZ2UgMjAyMy0xMi0wMiBhdCAyMS4zOS4xNF83NWRiOTM0MS5qcGcpDQoNCks9Nw0KDQohW10oaW1hZ2VzL1doYXRzQXBwIEltYWdlIDIwMjMtMTItMDIgYXQgMjEuMzkuNDBfZDU2M2Q2NGMuanBnKQ0K